# ArchLang — full agent context (llms-full.txt) This is the complete context for driving **ArchLang**, a tiny declarative language that compiles a `.arch` floor-plan source file into a professional drawing (SVG/PNG/PDF/DXF). It follows the [llms.txt](https://llmstxt.org/) convention: `llms.txt` is the concise project map, and this `llms-full.txt` is the whole thing in one document — the language spec, the agent workflow, the CLI reference, and every diagnostic code — sized to drop into a system prompt. ArchLang is built for agents: **deterministic** (same source → byte-identical output), **pure** (no runtime, no IO in the compiler), and **self-correcting** (every error carries a machine code and a `fix`). Author, render, and verify entirely through the `arch` CLI — never hand-render SVG. Contents: 1. Language spec — the whole language in one page. 2. Agent workflow — the compile → fix → describe → gate loop, and how to repair plan topology. 3. CLI reference — every command, flag, and exit code. 4. Diagnostic catalog — every error and warning, each with a fix. --- ## 1. Language spec # ArchLang in one prompt ArchLang is a tiny declarative language that compiles a `.arch` source file into a professional floor-plan drawing (SVG/PNG/PDF/DXF). It is built for AI agents: deterministic (same source → identical output), pure (no runtime/IO), and self-correcting (every error carries a machine code and a `fix`). This page is everything you need to author it. Print it any time with `arch spec`. ## The 7 rules that matter 1. **Units are millimetres.** A 4-metre wall is `4000`, not `4`. Optional metric suffixes fold to mm: `4m`=4000, `3.5m`=3500, `40cm`=400, `20mm`=20. 2. **Origin is top-left; +x goes right, +y goes DOWN** (screen/SVG convention — *not* math y-up). 3. **Coordinates are `(x, y)` tuples; sizes are `WxH`** (e.g. `4000x3000`) or ` x ` with spaces. 4. **Doors and windows must lie ON a wall segment** (on its centerline), or you get a `W_DOOR_OFF_WALL` / `W_WINDOW_OFF_WALL` warning. 5. **String interpolation is `"{expr}"`** inside double quotes (e.g. `label "Unit {i}"`). 6. **Ids must be unique.** Omit `id=` to auto-generate one; give an `id` only when you reference it. 7. **Everything is expand-time and pure** — `let`/`for`/`if`/functions all evaluate during compile. ## Structure ```arch plan "Title" { units mm # required-ish settings come first grid 50 # snap grid in mm scale 1:50 # drawing scale (annotation only) north up # up | down | left | right # … elements and scripting … title { project "…" drawn_by "…" date "…" } } ``` ## Elements ```text wall thickness [material ] { (x,y) (x,y) … [close] } # category e.g. exterior/partition; `close` makes a loop room [id=] at (x,y) size x [label "…"] [uses living|kitchen|dining|bedroom|bath|wc|hall|circulation|storage|utility|office|entry …] # OR relational: room [id=…] (right-of|left-of|below|above) [align top|middle|bottom|left|right] [gap ] size x [label "…"] door [id=] (at (x,y) | on at ) width [wall ] [hinge left|right|near start|near end] [swing in|out|into ] # `at (x,y)` must sit on a wall; `on at ` pins it BY CONSTRUCTION ( = `40%` | mm from the wall's start | `center`) and can never be reported off-wall — prefer it window [id=] (at (x,y) | on at ) width [wall ] # same two placement forms as door opening [id=] (at (x,y) | on at ) width [wall ] # a leaf-less cased opening (gap in a wall) that still connects the two spaces in the access graph furniture [id=] (at (x,y) | against wall [segment ] [offset ] [side left|right] | in centered | in anchor [inset ]) [size x] [label "…"] [rotate 0|90|180|270] [in ] # `at` size is plan W×H; `against` size is wall-relative along×depth and derives position+rotation, with `side` inferred from `in ` when omitted; a known fixture (wc/basin/shower/bathtub/kitchen_sink/counter/stove/fridge…) `against wall` may omit `size` to use its catalogued footprint. `anchor ` is top-left|top|top-right|left|center|right|bottom-left|bottom|bottom-right; `inset` (default 0) pulls it in from that edge dim (x,y)->(x,y) offset [text "…"] # a dimension line column [id=] at (x,y) size x strip at (x,y) gap [height|width ] { room [id=] size
[x] [label "…"] [uses …] … } # a row/column of rooms laid end to end: each room's offset is the running sum of the previous extents + gap, and the shared cross dimension is the strip's height (right/left) or width (down/up). Pure sugar — expands to absolute rooms. Plan-level block only ``` ## Scripting (all expand-time, deterministic) - `let NAME = expr` — bind a constant. `NAME = expr` — reassign an existing binding. - `let f(a, b) = expr` — a pure value-function. Built-ins: `min max abs sqrt floor ceil round len str`. - `for i in lo..hi { … }` — loop over a half-open integer range (`0..3` → 0,1,2). - `if cond { … } else { … }` · `while cond { … }`. - `set (attr: value)` — scoped default for following elements (e.g. `set door(swing: out)`). - Arrays: `[a, b, c]`, indexed `arr[i]`. Operators: `+ - * / %`, `== != < > <= >=`, `&& ||`. Comments: `# …`. - `import "lib/x.arch": name` and `component name(args) { … }` for reuse. ## Keyword reference (Elements are fully specced above; these are the rest.) - **Settings / control:** `plan`, `component`, `let`, `theme`, `title`, `style`, `import`, `for`, `if`, `while`, `else`, `set`, `strip` - **Attributes:** `units`, `grid`, `scale`, `north`, `dims`, `accTitle`, `accDescr`, `material`, `angle`, `at`, `size`, `width`, `thickness`, `label`, `hinge`, `swing`, `offset`, `text`, `close`, `id`, `project`, `drawn_by`, `date`, `from`, `as`, `right-of`, `left-of`, `below`, `above`, `align`, `gap`, `uses`, `rotate`, `against`, `segment`, `side`, `on`, `into`, `near`, `anchor`, `inset`, `height` - **Enums / values:** `up`, `down`, `left`, `right`, `in`, `out`, `mm`, `true`, `false`, `top`, `middle`, `bottom`, `center`, `centered`, `start`, `end`, `top-left`, `top-right`, `bottom-left`, `bottom-right`, `auto`, `living`, `kitchen`, `dining`, `bedroom`, `bath`, `wc`, `hall`, `circulation`, `storage`, `utility`, `office`, `entry` ## CLI loop (how an agent drives it) Every command takes `--json` (structured result on **stdout**, human messages on **stderr**) and reads source from a file or stdin (`-`). Exit codes: `0` ok · `1` internal / IO error · `2` user-source error (deterministic — fix it, don't blindly retry) · `3` bad usage. ```text arch compile # render a plan to SVG/DXF/TXT/PDF/PNG arch batch # render many .arch files in one call, concurrently arch md # render every ```arch block in a Markdown file and rewrite to image links arch preview # render a PNG you can look at (zero-install where the optional binary is present) arch watch # recompile on save (interactive) arch validate # parse + resolve + lint, no render (is it valid & sound?) arch describe # semantic facts: rooms, areas, adjacency, what doors connect arch score # continuous intent satisfaction (satisfied/total) as data — the refine-loop reward arch lint # architectural soundness warnings arch ast # parse only (no resolve/render) and print the span-bearing AST as JSON arch complete # completion items in scope at a source byte offset (the LSP completion() core) arch fmt # canonical formatting arch repair # explicit source-to-source corrector (furniture out of walls) + change log arch fix # apply the machine-applicable fix suggestions on a plan's diagnostics (bounded fixpoint) arch suggest # advisory topology suggestions as data (door/window statements that resolve reachability/window faults) arch manifest # this document: the whole CLI API as structured data arch spec # print the one-prompt language spec (spec.llm.md) arch context # print the full bundled agent context (spec + workflow + CLI + errors) arch new # scaffold a starter .arch arch explain # look up an error code (cause / fix / example) ``` The flags that matter (the verb list above covers the rest): ```bash arch compile plan.arch -o out.svg --json # JSON: { ok, diagnostics, summary }. -f txt = zero-dep ASCII plan echo '' | arch compile - --json # stdin, no temp file arch validate plan.arch --strict --json # ship-gate: --strict fails on warnings too arch fix plan.arch --dry-run --json # preview/apply the machine-applicable diagnostics[].fixes arch validate plan.arch --intent brief.json --feedback --json # gate on a brief's intent contract (miss → exit 2) arch score plan.arch --brief brief.json --json # satisfied/total — measures, never gates ``` **Self-correction loop:** compile/validate → if `ok` is false, read each `diagnostics[].fix` (and `line`/`col`/`span`), edit the source, recompile. Exit code `2` means a deterministic user-source error (fix it; don't blindly retry). Then `describe --json` to confirm the plan matches intent (right room count, areas, adjacency) without rendering an image. **Before shipping, gate with `arch validate --strict --json`** — it fails on advisory warnings too, so a plan that lint flags (furniture through a wall, a fixture blocking a doorway, a room you can't step into, an unreachable room, a walk that squeezes too narrow — `W_PATH_TOO_NARROW` — or wanders the long way round — `W_CIRCUITOUS_PATH`) cannot pass silently. **Place furniture so it's physically sound:** keep every piece inside its room and off the walls (don't cross a wall centerline); back plumbing/kitchen fixtures onto a wall with `against wall ` (+ `in `) rather than guessing an `at`; give every room a `door`/`opening`; and leave the doorway approach and the door's swing clear. **Fix topology from facts, not guesses.** `arch repair` corrects furniture but never adds a door or window (that is a design choice). When lint reports `W_ROOM_UNREACHABLE`, `W_NO_ENTRANCE`, `W_BEDROOM_NO_WINDOW`, or `W_BATH_VIA_BEDROOM`, run `arch suggest --json` — it returns ready-to-paste `door`/`window` statements (furniture-aware: a door candidate never opens onto a wardrobe) that reference a wall only by a stable ref (an authored id or a unique category) or absolute coordinates — never a re-bindable positional auto-id — with a rationale; pick one and insert it. If nothing fits, read `describe --json` (`access.rooms[].reachable`, room `bbox`/`adjacent`, building extent = min/max of room boxes) and attach the opening yourself — an exterior entrance into a cut-off living space beats routing a bath through a bedroom — then re-`repair` and `validate --strict`. See SKILL.md for the full recipe. ## Common mistakes | Mistake | Fix | | --- | --- | | Using metres (`size 4x3`) | Use millimetres (`size 4000x3000`). | | Expecting +y to go up | +y goes **down**; a room below another has a larger y. | | Door/window floating off its wall | Attach it: `door on at ` — hosted by construction, it can never be off-wall. | | Hand-summing room offsets | Lay the row with `strip` — each room's `at` is computed for you. | | Furniture floated at a guessed (or copy-pasted) `at` | Place it `in anchor <9-point> [inset]` or `against wall ` — closed-form, never floats or penetrates. | | `size 4000` (no height) | Sizes are `WxH`: `size 4000x3000` (or `W x H` with spaces). | | Reusing an `id` | Ids are unique; omit `id=` to auto-generate. | | String math without interpolation | Use `"{expr}"`, e.g. `label "{aream2(W,H)} m²"`. | ## Worked examples ### `examples/attached.arch` ```arch # A one-bedroom flat authored with the v1.13 placement sugar — no hand-computed # coordinates for openings or furniture. It exercises, together: # • `strip` — a row of rooms laid out end to end # • openings attached to a wall by position (`door|window … on at `) # • a door that opens toward a named room (`swing into`) hinged at a wall end # • furniture placed relative to a room (`in anchor …`) # Compiles clean and passes `arch lint`. plan "Attached 1BR" { units mm grid 100 north up # Living + bedroom laid left-to-right by a strip, sharing a 4 m depth. strip right at (0,0) gap 0 height 4000 { room id=r_living size 4000 label "Living" uses living room id=r_bed size 3000 label "Bedroom" uses bedroom } # Exterior shell as four straight walls (each a clean start→end to attach onto) # plus the partition between the two rooms. wall id=w_north exterior thickness 200 { (0,0) (7000,0) } wall id=w_south exterior thickness 200 { (0,4000) (7000,4000) } wall id=w_west exterior thickness 200 { (0,0) (0,4000) } wall id=w_east exterior thickness 200 { (7000,0) (7000,4000) } wall id=w_part partition thickness 100 { (4000,0) (4000,4000) } # Entrance 2000 mm along the south wall, hinged at that wall's start end and # opening into the living room. The bedroom door on the partition opens inward. door id=d_main on w_south at 2000 width 1000 hinge near start swing into r_living door id=d_bed on w_part at 2000 width 900 swing into r_bed # A window centred on each room's exterior wall. window on w_west at 50% width 1400 window on w_east at 50% width 1200 # Furniture placed by anchor inside each room (never off a coordinate). furniture sofa in r_living anchor top-left inset 300 size 2000x900 label "Sofa" furniture bed in r_bed anchor right inset 300 size 1500x2000 label "Bed" } ``` ### `examples/parametric.arch` ```arch # Parametric plan (v0.8 scripting): a row of studio units generated with a # `for` loop over a range, a value-function, an array indexed per unit, a scoped # `set` rule, an `if`, and string-interpolated labels. Everything is derived # from the constants — change COUNT and the whole row regenerates. plan "Parametric — Studio Row" { units mm grid 50 scale 1:100 north up # Plan-level constants (visible everywhere below — plan scope is global). let WALL = 200 let W = 4000 # unit width let H = 5000 # unit depth let DOOR = 900 let WIN = 1600 let COUNT = 3 # number of units # A value-function (pure closure) — area in square metres. let aream2(w, h) = w * h / 1000000 # Per-unit names, indexed by the loop variable. let names = ["Studio A", "Studio B", "Studio C"] # Entrance doors swing outward throughout this plan (scoped default). set door(swing: out) for i in 0..COUNT { let x = i * W wall exterior thickness WALL { (x, 0) (x + W, 0) (x + W, H) (x, H) close } room at (x, 0) size W x H label "{names[i]}" furniture bed at (x + 300, 300) size 1500x2000 label "Bed" furniture kitch at (x + W - 1900, 300) size 1600x600 label "Kitchen" door at (x + W / 2, H) width DOOR wall exterior hinge left window at (x + W / 2, 0) width WIN wall exterior # The end unit carries a per-unit area dimension (computed by the function). # Referenced to the outer face (y = H + WALL/2) so the extension lines start at # the wall and read downward, away from the building. if i == COUNT - 1 { dim (x, H + WALL / 2)->(x + W, H + WALL / 2) offset 600 text "{aream2(W, H)} m² each" } } # Overall run, dimensioned above the building: right-to-left so the offset lands # ABOVE the row (outside), and referenced to the outer top face (y = -WALL/2). dim (W * COUNT, 0 - WALL / 2)->(0, 0 - WALL / 2) offset 1300 text "{COUNT} units" } ``` --- ## 2. Agent workflow # ArchLang — author floor plans as code ArchLang turns a small `.arch` text file into a professional floor-plan drawing. It is built for agents: deterministic, self-correcting (errors carry a machine code, a prose `fix`, and often a **machine-applicable** fix `arch fix` can apply), and verifiable without ever looking at an image (`arch describe`). ## Setup (zero-install) The CLI runs straight from npm — no clone, no build: ```bash npx @chanmeng666/archlang help ``` (Or `npm i -g @chanmeng666/archlang` to get a persistent `arch` binary.) ## The loop (always follow this) 1. **Learn the language first.** Run `arch spec` and read it — the entire language in one page (~2k tokens). (`arch context` prints *everything*: spec + this workflow + CLI reference + error catalog.) Do this before writing any `.arch`. 2. **Write the plan** to a `.arch` file (or pipe via stdin with `-`), preferring the **placement sugar** below so you never hand-compute a coordinate. 3. **Render it:** `arch compile plan.arch -o plan.svg --json`. The JSON is `{ ok, diagnostics, summary }`. 4. **Auto-fix the mechanical faults:** if `ok` is false, run `arch fix plan.arch --dry-run --json` to preview the **machine-applicable** edits (off-wall opening → attachment form, out-of-range position clamped, …), then re-run without `--dry-run` to apply. Anything `fix` can't resolve stays in `diagnostics[].fix` for you to edit by hand. Exit code `2` means a deterministic user error — fix it, don't blindly retry (`1` = IO/internal, `3` = bad usage). 5. **See the plan without an image:** `arch compile plan.arch -f txt` (or `arch preview plan.arch --ascii`) prints a zero-dependency ASCII floor plan you can read straight from stdout. 6. **Verify intent:** `arch describe plan.arch --json` returns the rooms (areas, adjacency), what each door connects, and totals. Confirm the room count, labels, and areas match what was asked. 7. **Gate on soundness — don't ship a flagged plan.** `arch validate plan.arch --strict --json` (parse + resolve + lint). `--strict` makes **every advisory warning fail** (exit `2`) — the gate a generation pipeline runs before it ships. Add `--graph g.json` to also assert the intended room-to-room adjacency (`{ "living": ["kitchen","hall"], … }`); a mismatch fails. Read each `diagnostics[].fix`, edit, and re-run until it passes — or, if a warning is deliberate, say so. 8. **Check the plan against the brief.** Write the user's brief as an `intent.json` — its checkable expectations as data (room count, per-room concepts with area/window bands, total area, optional adjacency/reachability). Two disciplines keep it brief-grounded: assert an area band only where the brief gives a number ("about/~N" → ±10%; "at least N" → `min` only; qualitative words → nothing), and assert the top-level room `count` only when the brief **enumerates** the rooms. Gate with `arch validate plan.arch --intent intent.json --feedback --json`: a gating miss (room count/existence/area/total-area/window) fails (exit `2`) with a per-violation correction prompt — iterate on the feedback and re-run. Adjacency/reachability are advisory (reported, never fail the gate). Use `arch score plan.arch --brief intent.json --json` as a continuous satisfaction meter (always exit `0`) to watch the plan approach the brief across edits. See [`/intent.schema.json`](https://archlang.uk/intent.schema.json). 9. **Fix furniture geometry:** `arch repair plan.arch -o fixed.arch` pushes furniture out of walls/doorways/swing arcs (the geometric corrector; distinct from `fix`). 10. **Show the user:** `arch preview plan.arch -o plan.png` renders a PNG (`--install` fetches the optional renderer if missing). ## Write it right the first time (placement sugar — the preferred path) A geometry-blind generator that emits absolute coordinates produces plans that render but are physically wrong (openings off their wall, furniture through walls). Author by **attachment** instead — the compiler computes the coordinate, and fails loudly if the reference is ambiguous: - **Attach openings to a wall by position, not `at (x,y)`.** `door on at …` / `window on at …` / `opening on at …`, where `` is millimetres along the wall or a percentage (`50%`). `swing into ` picks the swing direction toward a named room; `hinge near start|end` hinges at the segment end nearer a wall end. (Off-wall/ambiguous → `E_ATTACH_WALL_REF`; past the wall → `E_ATTACH_POS_RANGE`.) - **Lay rooms with `strip`.** `strip right at (0,0) gap 0 height 4000 { room … room … }` places a row (or column, with `down`/`up` + `width`) of rooms end to end — no per-room `at`. - **Place furniture by anchor.** `furniture in anchor <9-point anchor> [inset ] …` snaps a piece flush to a room corner or edge — the anchor is one of `top-left`, `top`, `top-right`, `left`, `center`, `right`, `bottom-left`, `bottom`, `bottom-right`; `against wall ` backs plumbing/kitchen fixtures onto a real wall face. Both are closed-form and never float or penetrate. - **Every room still needs a way in** — put a `door` or cased `opening` on a wall of *every* room (an open-plan space still needs a modeled opening), and keep furniture out of the doorway approach (≥300 mm) and the leaf's swing. - **Absolute `at (x,y)` is the fallback**, not the default — reach for it only when no attachment expresses what you mean. See `examples/attached.arch` for a full one-bedroom authored this way, and `arch spec` for the grammar. ## Self-correct with data, not guesswork `arch compile --json` returns every problem as a `Diagnostic` with a byte span, `line`/`col`, a catalogued `E_*`/`W_*` code, and a prose `fix`. Where the correction is a mechanical text edit, the diagnostic also carries **machine-applicable `fixes`**: - **`arch fix`** applies them in a bounded, self-checking fixpoint — **only `machine-applicable` by default** (`--unsafe` also applies `maybe-incorrect`; `--dry-run` previews; `--force` keeps a pass that would otherwise roll back). Use it to clear the syntactic faults before you touch anything by hand. - **`arch fix` is syntactic; `arch repair` is geometric.** `fix` rewrites text where the right text is known (e.g. an off-wall door → the attachment form); `repair` *moves furniture* to a position no text edit could express. They compose — fix first, then repair. - **`arch fix` also applies fix-carrying *lint* advisories**, not only compile-stage faults — e.g. `W_ALIAS_MATCH` (a room's use inferred from an indirect label alias) fixes by inserting the explicit `uses …` it inferred. Before editing, `arch describe --json`'s **`freedom`** block tells you which element positions were **hand-authored** (`absolute`) vs **derived** by the resolver (relational/strip/attached/anchored/against-wall), so you know which numbers are safe to nudge. ## Fix the topology: add doors & windows the room graph needs `fix`/`repair` never add a door or a window — *where* to put one is a design choice the compiler must not make. When lint reports `W_ROOM_UNREACHABLE`, `W_ROOM_DISCONNECTED`, `W_NO_ENTRANCE`, `W_BATH_VIA_BEDROOM`, or `W_BEDROOM_NO_WINDOW`, ask ArchLang for candidates: - **`arch suggest plan.arch --json`** returns ready-to-paste `door`/`window` statements (furniture-aware — a door candidate never opens onto a wardrobe; each references its wall by a **stable ref** — an authored id or a unique category — or absolute coordinates, never a re-bindable positional id) plus a rationale for each — for a room with no path back (`W_ROOM_UNREACHABLE`), a building with no way in (`W_NO_ENTRANCE`), a bath reachable only through a bedroom (`W_BATH_VIA_BEDROOM`), or a windowless bedroom (`W_BEDROOM_NO_WINDOW`). Choose one and insert it, then re-run the loop. This replaces hand-computing coordinates. - **Manual fallback** (if `suggest` offers nothing that fits): from `describe().access`, connect each unreachable room in priority — (1) a new **exterior entrance** `door on at ` into a living/kitchen/hall with an exterior edge (avoids routing through a bedroom); else (2) a `door on at ` to an adjacent reachable, non-bedroom room; and give a windowless bedroom a `window on at width 1200`. Never make a bathroom reachable only through a bedroom. Then `arch repair` (a new door may pinch furniture) and re-gate. > An *existing* opening `validate` reports **off its wall** (`W_DOOR_OFF_WALL` / > `W_WINDOW_OFF_WALL` / `W_OPENING_OFF_WALL`) is a mis-coordinate, not a missing connector — run > `arch fix` (it rewrites it to the attachment form) rather than adding a new one. ## Ask the CLI, and read only what you need The CLI documents itself, and every read can be narrowed at the source — never pull a whole plan's facts into context just to filter them yourself. - **`arch --help`** (or `arch help `) prints that one command's flags *and worked examples* — every command carries at least one copy-pasteable invocation. `arch help` lists the commands; `arch --version` prints the version. A flag a command doesn't take is a usage error (exit `3`) with a did-you-mean, never a silently-swallowed filename — so a typo fails loudly instead of compiling the wrong thing. - **`arch describe --select `** emits only the named top-level keys (`rooms`, `doors`, `windows`, `openings`, `furniture`, `access`, `circulation`, `totals`, `freedom`, `caption`, …); the `ok`/`plan`/`units`/`diagnostics` envelope is always kept, so narrowing can't lose the verdict. **`arch describe --room `** keeps only those rooms plus the doors/windows/furniture that touch them (whole-plan facts — `bbox`, `totals`, `caption`, each room's `adjacent` — stay whole-plan, so a narrowed read never lies about the building). Both mark the result with `filtered: true`. - **`arch lint|validate --code ` / `--severity error|warning`** show only the diagnostics you asked for. These are **display filters only**: `ok` and the exit code are always computed from the *unfiltered* set, so reading less can never turn a failing plan green. A filtered result carries `filtered: true` + `total_diagnostics`. - **`arch context --section spec|workflow|cli|errors`** prints one section of the ~50 KB bundle instead of all of it — `errors` for the diagnostic catalog, `cli` for the command reference. - **`arch fix --dry-run`** prints the exact unified diff it would write (to stderr; `--json` also carries it as `diff`) and touches nothing. When you do apply in place, **`--backup`** keeps the original bytes at `.bak`. ## Structured authoring & constrained generation (optional) - **Plan JSON.** Author or ingest the machine-native shape and compile it: `arch compile plan.json --from-json -o out.svg`. The schema is served at [`/plan.schema.json`](https://archlang.uk/plan.schema.json). - **GBNF.** To force a local model to emit only parseable ArchLang, constrain decoding with [`/archlang.gbnf`](https://archlang.uk/archlang.gbnf). ## Commands ```bash arch spec # the whole language in one page — READ THIS FIRST arch context # everything in one call: spec + this workflow + CLI reference + error catalog arch context --section errors # just one section of it (spec|workflow|cli|errors) arch help # flags + worked examples for one command (same as `arch --help`) arch manifest --json # the whole CLI API as data: commands, flags, formats, lint rules, error codes arch compile plan.arch -o out.svg --json # render (also -f dxf|txt|pdf|png) arch compile plan.arch -f txt # zero-dependency ASCII text plan on stdout (also `preview --ascii`) arch compile plan.json --from-json -o out.svg # compile structured Plan JSON (see /plan.schema.json) echo '' | arch compile - -o - -f svg # compile stdin → SVG on stdout arch fix plan.arch --dry-run --json # preview the machine-applicable fixes as a unified diff (drop --dry-run to apply; --backup keeps .bak) arch suggest plan.arch --json # advisory door/window statements: unreachable room / no entrance / bath-via-bedroom / windowless bedroom arch describe plan.arch --json # semantic facts: rooms, areas, adjacency, door connections, circulation arch describe plan.arch --select rooms,totals --room kitchen --json # narrow the facts to what you actually need arch lint plan.arch --json # architectural soundness warnings arch lint plan.arch --code W_NO_ENTRANCE --json # display filter only — never changes `ok` or the exit code arch validate plan.arch --strict --json # parse + resolve + lint; --strict fails on warnings (the ship gate) arch validate plan.arch --graph g.json --json # also check interior-door adjacency against an intended graph arch repair plan.arch -o fixed.arch # geometric corrector: furniture out of walls/doorways/swings + change log arch fmt plan.arch --write # canonical formatting arch batch a.arch b.arch -f svg --json # render many plans/variants at once → results[] arch preview plan.arch -o plan.png # render a PNG to SHOW the user (--install fetches resvg if missing) arch new -o plan.arch # scaffold a starter plan arch explain E_ROOM_SIZE --json # look up any diagnostic code ``` (An optional MCP server, `@chanmeng666/archlang-mcp`, wraps these same library functions for MCP-native hosts — prefer the CLI when you have a shell; it costs nothing in context until called.) ## Key rules (full detail in `arch spec`) - **Units are millimetres** (a 4 m wall is `4000`); **origin top-left, +x right, +y DOWN**. An optional metric suffix is exact sugar for the same mm value — `4m` = `4000`, `40cm` = `400`, `20mm` = `20` — so you can write `4m` instead of hand-multiplying; bare numbers are unchanged. - **Attach openings to walls** (`on at `) so they always sit on a segment; a raw `at` that lands off any wall warns (and `arch fix` rewrites it). - **Fixtures draw real symbols:** `furniture wc|basin|shower|bathtub|kitchen_sink|counter|fridge|stove …` renders a plan symbol; put fixtures in every bath and kitchen so lint stays quiet. - **`dims auto`** draws dimension strings for you (`overall`, `rooms`, `walls`, or `all`). - Edit is cheap: "make the bedroom 1 m wider" is a one-number change, then recompile. Treat the CLI as the source of truth — author, render, and verify through it rather than reasoning about SVG by hand. --- ## 3. CLI reference The `arch` CLI is the agent interface. ArchLang compiler — agent-native CLI. Compile .arch floor-plan source to SVG/PNG/PDF/DXF. Every command takes `--json` (structured result on stdout, messages on stderr) and reads source from a file or stdin (`-`). This CLI is the primary agent interface; an optional stdio MCP shim (`@chanmeng666/archlang-mcp`) wraps the same library functions for MCP-native hosts. **Exit codes:** `0` ok · `1` internal / IO error · `2` user-source error (deterministic — fix it, don't blindly retry) · `3` bad usage **Global flags:** `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr **Output formats (`-f`):** `svg` · `dxf` · `txt` · `pdf` (needs `pdfkit`) · `png` (needs `@resvg/resvg-js`) ### Commands **`arch compile`** — render a plan to SVG/DXF/TXT/PDF/PNG - input: (Plan JSON with --from-json) → output: file (or stdout with -o -) - flags: `--out|-o ` output file, or '-' for stdout (default: the input path with the format's extension) · `--format|-f ` output format (default svg) · `--width|-w ` page width hint in pixels · `--scale|-s ` raster scale for the PNG backend (ignored by the non-raster formats) · `--cols ` text renderer (-f txt / preview --ascii) grid width in characters (default 80) · `--charset ` text renderer glyph set (default unicode) · `--overlay ` draw an opt-in diagnostic overlay (circulation walks + bottleneck markers); default output is unchanged · `--error-svg` on a broken plan, still emit a self-describing error-card image listing the diagnostics (exit code stays 2) · `--accessible` emit /<desc>/role/aria accessibility metadata (the describe() caption) into the SVG; default output is unchanged · `--from-json` read the input as Plan JSON (RPLAN shape) instead of .arch, convert it, then compile · `--install` auto-install the optional dep for the chosen format if missing (PNG/PDF) · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch compile plan.arch --json` — render SVG next to the input; structured result on stdout **`arch batch`** — render many .arch files in one call, concurrently - input: <a.arch> <b.arch> … → output: one file per input; --json gives a results[] array - flags: `--out|-o <dir>` output DIRECTORY for every rendered file (default: alongside each input) · `--format|-f <svg|dxf|txt|pdf|png>` output format (default svg) · `--jobs|-j <n>` max concurrent renders (default: CPU count) · `--width|-w <px>` page width hint in pixels · `--scale|-s <n>` raster scale for the PNG backend (ignored by the non-raster formats) · `--cols <n>` text renderer (-f txt / preview --ascii) grid width in characters (default 80) · `--charset <unicode|ascii>` text renderer glyph set (default unicode) · `--overlay <circulation>` draw an opt-in diagnostic overlay (circulation walks + bottleneck markers); default output is unchanged · `--error-svg` on a broken plan, still emit a self-describing error-card image listing the diagnostics (exit code stays 2) · `--accessible` emit <title>/<desc>/role/aria accessibility metadata (the describe() caption) into the SVG; default output is unchanged · `--install` auto-install the optional dep for the chosen format if missing (PNG/PDF) · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch batch a.arch b.arch c.arch -o out/ --json` — render design variants concurrently; one result row per input **`arch md`** (aliases: `markdown`) — render every ```arch block in a Markdown file and rewrite to image links - input: <doc.md> → output: out.md + one image per block - flags: `--out|-o <out.md|->` rewritten Markdown file, or '-' for stdout (default: <name>.out.md) · `--format|-f <svg|png>` image format for the blocks (default svg) · `--width|-w <px>` page width hint in pixels · `--scale|-s <n>` raster scale for the PNG backend (ignored by the non-raster formats) · `--overlay <circulation>` draw an opt-in diagnostic overlay (circulation walks + bottleneck markers); default output is unchanged · `--error-svg` on a broken plan, still emit a self-describing error-card image listing the diagnostics (exit code stays 2) · `--accessible` emit <title>/<desc>/role/aria accessibility metadata (the describe() caption) into the SVG; default output is unchanged · `--install` auto-install the optional dep for the chosen format if missing (PNG/PDF) · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch md README.md -o README.out.md --json` — render the fenced arch blocks to SVGs and rewrite them to image links **`arch preview`** — render a PNG you can look at (zero-install where the optional binary is present) - input: <file.arch|-> → output: PNG file (or ASCII text on stdout with --ascii) - flags: `--out|-o <out.png|->` output PNG file, or '-' for stdout (default: <name>.png) · `--scale|-s <n>` raster scale (default 1; without -w/-s the page auto-targets ~1600px wide for legibility) · `--width|-w <px>` page width hint in pixels · `--ascii` print a zero-dependency ASCII text plan to stdout instead of a PNG · `--cols <n>` text renderer (-f txt / preview --ascii) grid width in characters (default 80) · `--charset <unicode|ascii>` text renderer glyph set (default unicode) · `--overlay <circulation>` draw an opt-in diagnostic overlay (circulation walks + bottleneck markers); default output is unchanged · `--error-svg` on a broken plan, still emit a self-describing error-card image listing the diagnostics (exit code stays 2) · `--install` auto-install @resvg/resvg-js if missing, then render · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch preview plan.arch --ascii --json` — a zero-dependency text plan an agent can read on stdout **`arch watch`** — recompile on save (interactive) - input: <file.arch> → output: file, rewritten on each save - flags: `--out|-o <file|->` output file, or '-' for stdout (default: the input path with the format's extension) · `--format|-f <svg|dxf|txt|pdf|png>` output format (default svg) · `--width|-w <px>` page width hint in pixels · `--scale|-s <n>` raster scale for the PNG backend (ignored by the non-raster formats) · `--cols <n>` text renderer (-f txt / preview --ascii) grid width in characters (default 80) · `--charset <unicode|ascii>` text renderer glyph set (default unicode) · `--overlay <circulation>` draw an opt-in diagnostic overlay (circulation walks + bottleneck markers); default output is unchanged · `--error-svg` on a broken plan, still emit a self-describing error-card image listing the diagnostics (exit code stays 2) · `--accessible` emit <title>/<desc>/role/aria accessibility metadata (the describe() caption) into the SVG; default output is unchanged · `--from-json` read the input as Plan JSON (RPLAN shape) instead of .arch, convert it, then compile · `--install` auto-install the optional dep for the chosen format if missing (PNG/PDF) · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch watch plan.arch -o plan.svg` — recompile on every save (interactive; agents should use compile) **`arch validate`** — parse + resolve + lint, no render (is it valid & sound?) - input: <file.arch|-> → output: diagnostics (plus a graph{} report with --graph and an intent{ ok, satisfied, total, subscores, violations } block with --intent) - flags: `--strict|--fail-on-warning` advisory warnings fail too (exit 2) · `--graph <graph.json>` also check the plan's interior-door adjacency against an intended graph (bare dict or {input_graph:{…}}); mismatch → exit 2 · `--intent <intent.json>` gate the plan against a brief's intent JSON; a failing gating assertion (room count/existence/area/windows) → exit 2. Adjacency/reachability score but never gate. Composes with --graph. · `--feedback` with --intent, append a deterministic per-violation correction prompt (advisory data, never applied) · `--code <CODE[,CODE…]>` show only diagnostics with these codes — a DISPLAY filter: the exit code and `ok` still come from the unfiltered set · `--severity <error|warning>` show only diagnostics of this severity — a DISPLAY filter, like --code (never changes the exit code) · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch validate plan.arch --strict --json` — the ship gate: errors and advisory warnings both fail **`arch describe`** — semantic facts: rooms, areas, adjacency, what doors connect - input: <file.arch|-> → output: facts (JSON or a summary), narrowed by --room/--select - flags: `--room <id[,id…]>` keep only these rooms; doors/windows/openings/furniture narrow to the ones touching them (plan-level facts — bbox, totals, caption — stay whole-plan) · `--select <key[,key…]>` emit only these top-level keys of the --json object (rooms, doors, totals, access, circulation, freedom, …); the ok/plan/units/diagnostics envelope is always kept · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch describe plan.arch --json` — rooms, areas, adjacency, door connections, caption, freedom — confirm the plan means what you intended **`arch score`** — continuous intent satisfaction (satisfied/total) as data — the refine-loop reward. Measures, never gates (validate --intent is the gate). - input: <file.arch|-> → output: { ok, satisfied, total, score, subscores, violations } (exit 0 on a successful measurement, even when assertions fail) - flags: `--brief <intent.json>` the intent JSON to measure satisfaction against (required) · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch score plan.arch --brief brief.json --json` — continuous intent satisfaction as the refine-loop reward; always exits 0 on a measurement **`arch lint`** — architectural soundness warnings - input: <file.arch|-> → output: W_* warnings (narrowed by --code/--severity; `filtered`/`total_diagnostics` mark a filtered result) - flags: `--profile <residential-basic|accessibility-advisory>` advisory ruleset · `--strict|--fail-on-warning` warnings fail (exit 2) · `--code <CODE[,CODE…]>` show only diagnostics with these codes — a DISPLAY filter: the exit code and `ok` still come from the unfiltered set · `--severity <error|warning>` show only diagnostics of this severity — a DISPLAY filter, like --code (never changes the exit code) · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch lint plan.arch --json` — architectural soundness warnings as data, each with a fix **`arch ast`** — parse only (no resolve/render) and print the span-bearing AST as JSON - input: <file.arch|-> → output: AST JSON (scripting nodes unexpanded) - flags: `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch ast plan.arch --json` — span-bearing parse tree with no resolve or render — locate a statement by byte offset **`arch complete`** — completion items in scope at a source byte offset (the LSP completion() core) - input: <file.arch|-> → output: { items: [...] } completion items - flags: `--at <byteOffset>` source byte offset to list completions at (required) · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch complete plan.arch --at 120 --json` — what may legally be written at byte offset 120 **`arch fmt`** — canonical formatting - input: <file.arch|-> → output: formatted source (or in place with --write) - flags: `--write` format the file in place · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch fmt plan.arch --json` — canonical source plus a `changed` flag, nothing written **`arch repair`** — explicit source-to-source corrector (furniture out of walls) + change log - input: <file.arch|-> → output: corrected source + change log on stderr - flags: `--out|-o <file|->` output file for the corrected source, or '-' for stdout (default: stdout) · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch repair plan.arch --json` — the geometric corrector: `source` + a `changes[]` log of every furniture move **`arch fix`** — apply the machine-applicable fix suggestions on a plan's diagnostics (bounded fixpoint) - input: <file.arch|-> → output: fixed source (to the input file or -o) + a unified diff and change log on stderr - flags: `--out|-o <file|->` output file for the fixed source, or '-' for stdout (default: rewrite the input file in place) · `--unsafe` also apply `maybe-incorrect` fixes (default: machine-applicable only) · `--dry-run` compute the result but do not write it (the diff preview still prints) · `--backup` before rewriting a file in place, save the original bytes to <file>.bak · `--force` keep a pass even if it raises the error count · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch fix plan.arch --dry-run` — preview the exact unified diff `fix` would write, changing nothing on disk **`arch suggest`** — advisory topology suggestions as data (door/window statements that resolve reachability/window faults) - input: <file.arch|-> → output: suggestions (JSON or a summary) - flags: `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch suggest plan.arch --json` — ready-to-paste door/window statements for unreachable rooms, no entrance, or a windowless bedroom **`arch manifest`** (aliases: `capabilities`) — this document: the whole CLI API as structured data - input: none → output: the manifest (JSON or a summary) - flags: `--json` structured result on stdout, messages on stderr - example: `arch manifest --json` — discover every command, flag, format, and error code in one call **`arch spec`** — print the one-prompt language spec (spec.llm.md) - input: none → output: the spec - flags: `--json` structured result on stdout, messages on stderr - example: `arch spec` — the whole language on one page — read this before authoring **`arch context`** — print the full bundled agent context (spec + workflow + CLI + errors) - input: none → output: the full agent context (llms-full.txt), or one --section of it - flags: `--section <spec|workflow|cli|errors>` print only one section of the bundle instead of all ~50KB of it (spec = the language, workflow = the agent loop, cli = every command, errors = the diagnostic catalog) · `--json` structured result on stdout, messages on stderr - example: `arch context` — the cold-start bundle: spec + workflow + CLI reference + every diagnostic **`arch new`** (aliases: `init`) — scaffold a starter .arch - input: none → output: starter source - flags: `--out|-o <file|->` output file — refuses to overwrite an existing one without --force (default: stdout) · `--force` overwrite an existing file · `--json` structured result on stdout, messages on stderr · `--quiet|-q` suppress human messages on stderr - example: `arch new --json` — get the starter plan as a `template` string, writing nothing **`arch explain`** — look up an error code (cause / fix / example) - input: <CODE> → output: catalog entry - flags: `--json` structured result on stdout, messages on stderr - example: `arch explain E_ROOM_SIZE --json` — the catalog entry for a diagnostic code: cause, fix, example --- ## 4. Diagnostic catalog Every diagnostic carries a stable code and a `fix`. Look one up with `arch explain <CODE>`. **51 errors** (abort rendering) · **32 warnings** (advisory; `validate --strict` fails on them too). ### Errors - `E_ACC_PLACEMENT` — `accTitle`/`accDescr` used outside the plan level. **Fix:** Move the `accTitle`/`accDescr` line up to the plan body, alongside `units`/`north`. - `E_ARGCOUNT` — Component called with the wrong number of arguments. **Fix:** Pass exactly one argument per declared parameter. - `E_ARITY` — Built-in function called with the wrong number of arguments. **Fix:** Check the function's arity; most built-ins take one argument. - `E_ASSIGN_UNDEF` — Assignment to an undeclared name. **Fix:** Declare it first with `let`, or fix a typo in the name. - `E_ATTACH_POS_RANGE` — Opening attachment position is out of range. **Fix:** Use a percentage in 0–100%, a millimetre distance within the wall's run, or `center`. - `E_ATTACH_WALL_REF` — Opening attached to an unknown or ambiguous wall. **Fix:** Reference an existing, unique wall id (add `id=` to the wall if needed). - `E_CALL_DEPTH` — Value-function call stack too deep. **Fix:** Make the recursion terminate, or rewrite it iteratively with a bounded `while`. - `E_COLUMN_SIZE` — Column must have a positive size. **Fix:** Give the column a positive `size W x H`. - `E_DIV_ZERO` — Division or modulo by zero. **Fix:** Guard the divisor, or use a non-zero value. - `E_DOMAIN` — Math domain error. **Fix:** Pass a value within the function's domain. - `E_DOOR_WIDTH` — Door must have a positive width. **Fix:** Give the door a positive `width`. - `E_DUP_ID` — Duplicate element id. **Fix:** Rename one of them, or drop the explicit id to auto-generate a unique one. - `E_FURN_AGAINST` — Invalid `against wall` fixture placement. **Fix:** Name an existing wall id, add `segment <n>` for multi-segment walls, give `side left|right`, keep the segment axis-aligned, and drop any explicit `rotate`. - `E_FURN_ROOM` — Furniture placed `in` an unknown room. **Fix:** Use the id of an existing `room id=…`, or drop the `in` clause. - `E_FURN_ROTATE` — Furniture rotation must be a quarter-turn. **Fix:** Use a quarter-turn: `rotate 0|90|180|270`. - `E_FURN_SIZE` — Furniture must have a positive size. **Fix:** Give the item a positive `size W x H`. - `E_IMPORT_BAD_SPEC` — Malformed import spec. **Fix:** Use a relative path ("lib/x.arch") or a namespaced spec ("@scope/name:1.0.0"). - `E_IMPORT_CONFLICT` — Imported name conflicts with an existing component. **Fix:** Rename with `as`, or remove the duplicate. - `E_IMPORT_CYCLE` — Cyclic import. **Fix:** Break the cycle so module dependencies form a tree. - `E_IMPORT_NOT_EXPORTED` — Imported name is not exported by the module. **Fix:** Import a name the module actually defines (check its `component`s). - `E_IMPORT_NOT_FOUND` — Import path could not be resolved. **Fix:** Check the path (relative to the importing file) and that the file exists. - `E_IMPORT_PARSE` — Imported module has a parse error. **Fix:** Fix the syntax error in the imported module. - `E_INDEX` — Array index out of range. **Fix:** Clamp or check the index against `len(arr)`. - `E_INTENT_NO_DOOR` — The plan has no modeled entrance, so `reachable` cannot hold. **Fix:** Add an exterior entrance `door` on a perimeter wall. Advisory tier: reported and scored by `validateIntent` but does NOT fail `ok` (gate: false). - `E_INTENT_NO_WINDOW` — A room the brief wants a window in has too few. **Fix:** Add a `window` on one of that room's walls. Gating tier: this failure DOES fail `validateIntent`'s `ok`. - `E_INTENT_NOT_ADJACENT` — Two rooms the brief wants adjacent share no interior door. **Fix:** Add a `door` (or `opening`) on the wall the two rooms share so they are directly connected. Advisory tier: this is scored and reported by `validateIntent` but does NOT fail `ok` (gate: false) — one-shot topology is what the loop tools address. - `E_INTENT_ROOM_AREA` — A named room's floor area is outside the brief's band. **Fix:** Resize the room so its floor area lands in the band. Gating tier: this failure fails `validateIntent`'s `ok`. Assert a band only where the brief states a number — qualitative size words license none. - `E_INTENT_ROOM_COUNT` — The plan's room count does not match the brief. **Fix:** Add or remove rooms to reach the enumerated count. Gating tier: this failure fails `validateIntent`'s `ok`. - `E_INTENT_ROOM_MISSING` — A room the brief names is absent from the plan. **Fix:** Add a `room` whose label, `uses`, or type matches the concept. Gating tier: this failure fails `validateIntent`'s `ok`. - `E_INTENT_TOTAL_AREA` — The plan's total floor area is outside the brief's band. **Fix:** Grow or shrink rooms so the total lands in the band. Gating tier: this failure fails `validateIntent`'s `ok`. Assert a band only where the brief states a number. - `E_INTENT_UNREACHABLE` — A room cannot be reached from the entrance through modeled doors. **Fix:** Add interior doors so every room connects back to the entrance. Advisory tier: reported and scored by `validateIntent` but does NOT fail `ok` (gate: false). - `E_JSON_KIND` — Unknown element kind in plan JSON. **Fix:** Use one of the supported kinds: opening `kind` must be `door` | `window` | `opening`. - `E_JSON_SCHEMA` — Plan JSON does not match the schema. **Fix:** Fix the value at the reported JSON path (the message names it, e.g. `/rooms/0/width`); express geometry as concrete numbers, and author scripting/imports in `.arch` source instead. - `E_LAYOUT_CYCLE` — Relational room placement forms a cycle. **Fix:** Break the cycle by giving one of the rooms absolute `at (x,y)` coordinates. - `E_LAYOUT_REF` — Relational placement references an unknown room. **Fix:** Reference an existing room id, or fix the typo. - `E_OPENING_WIDTH` — Opening must have a positive width. **Fix:** Give the opening a positive `width`. - `E_PLACE_REF` — Furniture placed in an unknown or non-absolute room. **Fix:** Reference an existing room given absolute `at (x,y)` coordinates. - `E_PNG_DEPENDENCY` — PNG/PDF export needs an optional dependency that is not installed. **Fix:** Install the optional dependency (`npm install @resvg/resvg-js`), or re-run with `--install` to fetch it automatically, or render to SVG/DXF (zero-dependency). - `E_RANGE_LIMIT` — Range too large. **Fix:** Use a smaller range, or restructure to avoid materializing it. - `E_RECURSION` — Component recursion too deep. **Fix:** Add a base case so the recursion terminates. - `E_REDEF` — Name already defined in this scope. **Fix:** Rename one binding, or use `NAME = …` to reassign instead of redeclaring. - `E_ROOM_SIZE` — Room must have a positive size. **Fix:** Give the room a positive `size W x H`. - `E_STRIP_NEST` — Illegal `strip` nesting. **Fix:** Move the `strip` to the plan body, alongside the other elements. - `E_STRIP_SIZE` — Room in a `strip` is missing a size. **Fix:** Give the room a `size <main>` (main-axis extent) plus either a strip `height`/`width` or its own `size <main>x<cross>`. - `E_TYPE` — Type mismatch. **Fix:** Convert or supply the expected type. - `E_UNKNOWN_COMPONENT` — Unknown component. **Fix:** Define the component, import it, or fix the name (see the suggestion hint). - `E_UNKNOWN_FN` — Unknown function. **Fix:** Define it with `let f(…) = …`, or fix the name. - `E_UNKNOWN_REF` — Unknown reference. **Fix:** Declare it with `let`, pass it as a parameter, or fix the typo. - `E_WALL_THICKNESS` — Wall must have a positive thickness. **Fix:** Give the wall a positive `thickness`. - `E_WHILE_LIMIT` — `while` exceeded its iteration cap. **Fix:** Ensure the loop body updates a binding so the condition eventually fails. - `E_WINDOW_WIDTH` — Window must have a positive width. **Fix:** Give the window a positive `width`. ### Warnings - `W_ALIAS_MATCH` — A room's use was inferred from an indirect alias, not stated. **Fix:** Add an explicit `uses …` to the room stating the inferred function — the machine-applicable fix inserts it for you. This pins the classification without changing the room's `describe()` type. - `W_BATH_VIA_BEDROOM` — Bathroom is reachable only through a bedroom. **Fix:** Add a door connecting the bathroom to a hall/living space, or route circulation so it is not reached only via a bedroom. - `W_BEDROOM_NO_WINDOW` — Bedroom has no window. **Fix:** Add a `window` on an exterior wall of the room. - `W_CIRCUITOUS_PATH` — A room is reached by a very roundabout path. **Fix:** Add a more direct connection — a door or a hall — so the room is not reached the long way round. - `W_DOOR_CLEARANCE` — Door is narrower than the minimum clear width. **Fix:** Widen the door to at least the minimum clear width. - `W_DOOR_OFF_WALL` — Door does not lie on any wall. **Fix:** Move the door onto a wall, or name its host with `wall <id|category>`. The diagnostic points at the nearest wall. - `W_DOORWAY_BLOCKED` — A doorway's landing is blocked. **Fix:** Clear the space directly in front of and behind the door, or move the door. - `W_DUP_ACC_METADATA` — Duplicate `accTitle`/`accDescr`. **Fix:** Keep a single `accTitle` and a single `accDescr`; delete the extra line(s). - `W_EMPTY_PLAN` — Empty plan. **Fix:** Add at least one element (wall, room, …). - `W_FIXTURE_FLOATING` — A plumbing/kitchen fixture is not against a wall. **Fix:** Move the fixture so one edge is against a wall (supply/waste/venting runs in the wall), or remove it. - `W_FIXTURE_WRONG_ROOM` — Fixture sits outside its declared room. **Fix:** Move the fixture inside the named room, or correct the `in <roomId>`. - `W_FURN_CLEARANCE` — A fixture's use-space is blocked. **Fix:** Leave the catalogued clearance clear in front of the fixture, or move the obstructing furniture. - `W_FURNITURE_OVERLAP` — Two pieces of furniture overlap. **Fix:** Move or resize one so they no longer intersect; leave a walkway between them. - `W_FURNITURE_WALL_COLLISION` — Furniture penetrates a wall. **Fix:** Move or resize the piece so it sits fully inside the room (against the wall face, not through it), or anchor it with `against wall <id>`. - `W_HATCH_SCALE` — Hatch scale must be positive; using 1. **Fix:** Use a positive `scale`. - `W_NO_ENTRANCE` — The plan has no exterior door. **Fix:** Add a `door` on an `exterior` wall. - `W_OPENING_OFF_WALL` — Opening does not lie on any wall. **Fix:** Move the opening onto a wall, or name its host with `wall <id|category>`. The diagnostic points at the nearest wall. - `W_PATH_TOO_NARROW` — The walk to a room squeezes below a passable width. **Fix:** Widen the tightest door/opening on the route, or move the furniture pinching it, so the whole path clears the minimum width. - `W_ROOM_DISCONNECTED` — Room has no door — it can't be entered. **Fix:** Add a `door` on one of the room's walls. - `W_ROOM_NO_CLEAR_PATH` — A room cannot be entered or crossed. **Fix:** Open up the layout: move or shrink the furniture nearest the door so there is a continuous walkable strip from each entrance into the room. - `W_ROOM_NO_FIXTURE` — Bathroom or kitchen has no fixtures. **Fix:** Place the expected fixtures — e.g. import `lib/fixtures.arch` and add a `wc`, `basin`, `shower`, or `kitchen_sink`. - `W_ROOM_NOT_ENCLOSED` — Bathroom is not fully enclosed. **Fix:** Extend the partition so the room's perimeter is walled on all sides (a door/window in the wall is fine — only a missing wall counts). - `W_ROOM_OVERLAP` — Rooms overlap. **Fix:** Adjust positions/sizes if the overlap is unintended (it is allowed). - `W_ROOM_TOO_SMALL` — Room is implausibly small. **Fix:** Increase its `size`, or merge it into an adjacent space. - `W_ROOM_UNREACHABLE` — Room cannot be reached from the entrance. **Fix:** Add a door or cased `opening` linking it (directly or through a hall) to a space that reaches the entrance. - `W_SANITIZED_CONFIG` — A disallowed config value was stripped. **Fix:** Use a plain colour/string value (no `<`, `>`, or `url(data:…)`). - `W_SWING_OBSTRUCTED` — Door swing is obstructed. **Fix:** Move the door or the obstruction, flip the `hinge`/`swing`, or use a sliding door so the leaf clears. - `W_SWING_ROOM_NOT_ADJACENT` — `swing into <room>` names a room the door does not border. **Fix:** Point `swing into` at a room the door actually opens onto, or use explicit `swing in|out`. The door falls back to its default swing. - `W_UNKNOWN_MATERIAL` — Unknown wall material; using the default hatch. **Fix:** Use a known material (e.g. brick, concrete, insulation, tile) or omit it. - `W_UNKNOWN_STYLE_KEY` — Unknown style key. **Fix:** Use a valid key (e.g. fill / stroke / label, depending on the kind). - `W_UNKNOWN_THEME_KEY` — Unknown theme key. **Fix:** Use a known theme key (see the language reference / hover). - `W_WINDOW_OFF_WALL` — Window does not lie on any wall. **Fix:** Move the window onto a wall, or name its host with `wall <id|category>`. The diagnostic points at the nearest wall.