# Diagnostics

Every `ww::stage::name` code a compile can report, scanned from the compiler's sources. A scene carries them in `diagnostics` with a severity, the line, and the message. Where the stage declares a message template, it is shown (`{braces}` are filled in at run time) with its help line.

## Stage `analysis`

### `ww::analysis::unaccounted_contact`

Reported by the compiler with a message built at run time.

## Stage `check`

### `ww::check::below_floor`

Reported by the compiler with a message built at run time.

### `ww::check::empty_assembly`

Reported by the compiler with a message built at run time.

### `ww::check::extra_root`

Reported by the compiler with a message built at run time.

### `ww::check::floating`

Reported by the compiler with a message built at run time.

### `ww::check::slide_clearance`

Reported by the compiler with a message built at run time.

### `ww::check::travel_foul`

Reported by the compiler with a message built at run time.

### `ww::check::travel_invalid`

Reported by the compiler with a message built at run time.

### `ww::check::travel_unchecked`

Reported by the compiler with a message built at run time.

## Stage `dsl`

### `ww::dsl::bad_choice`

A `choice` set to a word its declaration does not allow, or declared without words. Message: ``choice `{choice}`: {problem}``

Help: `{allowed}`

### `ww::dsl::bad_import_path`

Message: `` `{path}` is not a usable import path ``

Help: `` `from=` is `std:name` for an embedded library, or a `/`-separated path relative to this file ``

### `ww::dsl::bad_reference`

Message: `` `{raw}` is not a valid reference ``

Help: ``a reference is `Part@Mark` (ADR-0002); either half may interpolate a name in braces (`Rail_{i}@Slot_{i}`), but braces must pair up around it``

### `ww::dsl::bad_value`

Message: `` `{node}` has a `{key}` of the wrong type ``

Help: `expected {expected}`

### `ww::dsl::collection_cycle`

Message: `collections extend each other in a cycle: {chain}`

Help: `` `extends=` must lead back to a collection with no `extends=` ``

### `ww::dsl::collection_unknown_key`

A derived collection setting a param, choice or slot its bases never declared. Message: ``collection `{collection}` sets {what} `{name}`, which `{base}` does not declare``

Help: `a derived collection lists only differences; declare it in the base collection first`

### `ww::dsl::duplicate`

Message: `` `{name}` is declared more than once in this scope ``

### `ww::dsl::import_conflict`

Message: `import conflict: {what}`

Help: `{hint}`

### `ww::dsl::import_cycle`

Message: `import cycle: {chain}`

Help: `a file cannot import itself, directly or through others`

### `ww::dsl::import_load_failed`

Message: ``cannot import `{path}`: {reason}``

### `ww::dsl::import_too_deep`

[`crate::Limits::max_import_depth`]. Message: `imports chain more than {limit} files deep`

Help: `a chain of files importing files may be at most {limit} long (docs/KDL_AUTHORING.md §13)`

### `ww::dsl::kdl_syntax`

Message: `file does not parse as KDL v2`

Help: ``woodwerx uses KDL v2. Note that `(` after `=` starts a type annotation, not a tuple, and backtick is a legal identifier character (ADR-0002).``

### `ww::dsl::missing_field`

Message: `` `{node}` is missing {what} ``

### `ww::dsl::missing_project`

Message: ``expected a single top-level `project` node``

### `ww::dsl::nothing_exported`

Message: `` `{path}` exports nothing ``

Help: `` a file opts in to being imported by writing `export=Name` on a top-level `assembly` or `component` ``

### `ww::dsl::slot_contract`

A slot bound to something that is missing, of the wrong kind, or lacks a param the slot's contract names. Points at the collection line that made the binding. Message: ``slot `{slot}` of collection `{collection}`: {problem}``

Help: `` a slot binds a name of its kind, and a component or assembly slot must declare every param in its `requires=` ``

### `ww::dsl::source_too_large`

[`crate::Limits::max_source_bytes`]. Checked before the file is parsed at all. Message: `the file is {size} bytes; the limit is {limit}`

Help: `a project file may be at most {limit} bytes (docs/KDL_AUTHORING.md §13). The largest example is about a sixth of that`

### `ww::dsl::too_deep`

[`crate::Limits::max_depth`], counted in `{ … }` blocks. Checked before the file is parsed, because the KDL parser recurses once per level. Message: `blocks nest more than {limit} deep`

Help: `` `{ … }` blocks may nest at most {limit} levels, counting `project` as the first (docs/KDL_AUTHORING.md §13) ``

### `ww::dsl::too_many_imports`

[`crate::Limits::max_imports`]. Message: `more than {limit} distinct files imported`

Help: `a project may pull in at most {limit} distinct files (docs/KDL_AUTHORING.md §13)`

### `ww::dsl::unexpected_entry`

An argument or property in a slot the typed IR has nowhere to put.  Stage 1 reports these rather than discarding them: silently dropping a property the author wrote is how a file compiles into something other than what it says. Message: `` `{node}` does not take {what} ``

Help: `{expected}`

### `ww::dsl::unexpected_node`

Message: `` `{node}` is not valid at this scope ``

Help: `{expected}`

### `ww::dsl::unknown_collection`

`project collection=X` or `extends=X` naming a collection that does not exist. Message: `` no collection named `{name}` ``

Help: `{available}`

### `ww::dsl::unknown_export`

Message: `` `{path}` does not export `{name}` ``

Help: `it exports: {available}`

### `ww::dsl::unknown_slot`

`@name` where no slot of that name is in force. Message: ``no slot `@{name}` in force``

Help: `{available}`

### `ww::dsl::unknown_unit`

A `(unit)` annotation on a number that is not `mm` or `in` (ADR-0021). Message: `` `({unit})` is not a known unit ``

Help: ``the unit annotations are `(mm)` and `(in)`; bare numbers are millimetres``

## Stage `eval`

### `ww::eval::cel_runtime`

Message: `expression could not be evaluated`

### `ww::eval::cel_syntax`

Message: `expression is not valid CEL`

Help: `{hint}`

### `ww::eval::cycle`

Message: `` `{name}` is defined in terms of itself ``

Help: `the cycle is: {cycle}`

### `ww::eval::expression_too_complex`

An expression has more operators and brackets than [`crate::guard::MAX_EXPR_OPERATORS`] — a bound on how deep its tree can nest, which `cel` walks recursively. Checked before it is parsed. Message: `expression has {count} operators and brackets`

Help: `at most {limit} in one expression; split it into named parameters`

### `ww::eval::expression_too_long`

An expression's source text is longer than [`crate::guard::MAX_EXPR_BYTES`]. Checked before it is parsed. Message: `expression is {len} bytes long`

Help: `at most {limit} bytes; split it into named parameters`

### `ww::eval::fractional_count`

Message: `` `count` is {value}, so this `repeat` runs {iterations} time(s) ``

Help: `` `repeat count=` is floored (ADR-0005). Write `ceil(…)` for {next_up}, or make the count come out whole ``

### `ww::eval::link`

A linked parameter (`solves=` / `inverse=`, ADR-0035) that cannot work: the target is derived, the link loops, or the inverse reads what it is trying to set. Message: `` `{name}` is a badly linked parameter: {message} ``

Help: ``a linked parameter is a derived one whose `solves=` names a literal parameter it depends on; see docs/adr/0035-linked-parameters.md``

### `ww::eval::non_positive`

Message: `` `{name}` is {value}, which is not a positive dimension ``

### `ww::eval::not_finite`

Message: `` `{name}` evaluates to {value} ``

Help: `a dimension must be a finite number; check for a division by zero`

### `ww::eval::not_in_language`

Something CEL parses but woodwerx's expression language does not include: a comprehension macro, a method call, a list or map, field selection. See [`crate::guard`]. Message: `{construct} is not part of woodwerx's expression language`

Help: `{hint}`

### `ww::eval::out_of_range`

Message: `` `{name}` is {value}, outside its declared range {min}..={max} ``

### `ww::eval::repeat_count_too_large`

`repeat count=` resolved to more iterations than Stage 2 will enumerate.  Stage 2 binds the loop index for each iteration, so a count is a bound on work. A furniture project does not have ten thousand of anything; a count that large is a units mistake or a runaway expression, and diagnosing it beats hanging. Message: `` `repeat` asks for {count} iterations ``

Help: `at most {limit} are resolved; check the units on this count`

### `ww::eval::text_too_long`

An expression can build text longer than [`crate::guard::MAX_TEXT_BYTES`]. Usually found before evaluation, from the lengths of what the expression concatenates, in which case `len` is that upper bound. Message: `` `{name}` can build {len} bytes of text ``

Help: `text in an expression is limited to {limit} bytes`

### `ww::eval::too_many_instances`

`Limits::max_parts`, reached by nested `repeat`s: this one's count times every enclosing one's. Diagnosed before the loop is unrolled, not after. Message: ``nested `repeat`s unroll to {product} instances; the limit is {limit}``

Help: ``this count multiplied by every enclosing `repeat`'s is {product}; a project may expand to at most {limit} parts (docs/KDL_AUTHORING.md §13)``

### `ww::eval::too_many_parts`

`Limits::max_parts`, reached by the `part` nodes walked with every `repeat` unrolled. Resolution stops here. Message: ``more than {limit} `part`s, counting each `repeat` iteration``

Help: `a project may expand to at most {limit} parts (docs/KDL_AUTHORING.md §13)`

### `ww::eval::too_many_specialisations`

`Limits::max_specialisations`: distinct override sets across instances of assemblies. Message: `more than {limit} distinct assembly specialisations`

Help: ``each distinct set of overrides on an `instance=` of an assembly is one specialisation; at most {limit} (docs/KDL_AUTHORING.md §13). An assembly that instances itself with changing overrides never settles``

### `ww::eval::unknown_name`

Message: `` unknown name `{name}` ``

### `ww::eval::unknown_override`

Message: `` `{key}` is not a parameter of `{component}` ``

Help: ``{}`{component}` declares no parameters, so it has nothing to override`{component}` declares: {},``

### `ww::eval::unsupported_operator`

An operator CEL parses but woodwerx's numeric domain cannot apply.  The one that matters is `%`: `cel` reports it as `UnsupportedBinaryOperator("rem", Float, Float)`, which tells an author writing `shelf_pitch % 2.0` nothing at all. No example in `examples/` uses it outside a comment, but an author will. Message: `` `{op}` cannot be used on dimensions ``

Help: `{hint}`

## Stage `expand`

### `ww::expand::assembly_anchor`

An assembly has no stock box, so of D6's anchors only the origin means anything. Message: `` an assembly instance cannot be anchored `by {anchor}` ``

Help: ``an assembly has no board to take a bounding-box anchor from, so it is placed by its own origin: `by top_sw`, or no `by` at all. Move the parts inside the assembly instead``

### `ww::expand::bad_dimension`

Message: `` `{what}` is {value}, which cannot be a dimension ``

Help: `a board has three finite, positive extents (D4)`

### `ww::expand::bad_interpolation`

Message: `` `{text}` cannot be interpolated ``

Help: `{help}`

### `ww::expand::bad_mark`

Message: `` `mark` needs {what} ``

### `ww::expand::bad_mirror`

RFC-0012 / proposal 0202: `mirror` reflects across exactly one axis. Message: `` `mirror` needs {what} ``

Help: `` `mirror` reflects the part across the plane `<axis> = <value>` in the parent frame: exactly one of `x=`, `y=` or `z=` (RFC-0012, proposal 0202) ``

### `ww::expand::bad_normal`

Message: `` `{found}` is not a face direction ``

Help: `` `normal=` is one of pos_x neg_x pos_y neg_y pos_z neg_z, pointing out of the material (D6) ``

### `ww::expand::bad_stock`

Message: `` `stock` is malformed ``

Help: `{help}`

### `ww::expand::bad_travel`

A `travel` child of a `join` that cannot be read (ADR-0075). Message: ``bad `travel`: {detail}``

Help: `` `travel [kind=slide|angle] [axis=x|y|z, optionally -signed] [min=] [max=] [rest=] [step=] [body=part|assembly] [pivot=<mark>]` ``

### `ww::expand::duplicate_axis`

`pos x=1 x=2`: KDL keeps the last, and the first would be lost without a word. Message: `` `{node}` gives `{key}=` twice ``

Help: ``each of `x=`, `y=`, `z=` appears at most once on a `{node}`; the second would silently replace the first``

### `ww::expand::duplicate_id`

Message: `` `{id}` names two things in one assembly ``

Help: `` ids are unique among the parts and sub-assemblies of one assembly. Inside a `repeat`, interpolate the loop variable: `id="Name_{i}"` ``

### `ww::expand::duplicate_mark`

Message: ``mark `{name}` is declared more than once on one part``

### `ww::expand::duplicate_node`

Message: `` `{node}` is given more than once ``

### `ww::expand::inline_override`

Message: `` `{key}` is not a parameter — part `{id}` instantiates no component ``

Help: `only a component instance takes parameter overrides; an inline part's expressions see the enclosing scope directly`

### `ww::expand::instance_cycle`

Message: ``assembly `{name}` contains itself``

Help: `the chain is: {cycle}`

### `ww::expand::instance_with_stock`

Message: `` part `{id}` has both `instance=` and its own `stock` ``

Help: ``a `part` either instantiates a component or carries an inline `stock`, never both (docs/KDL_AUTHORING.md §9). The component's `stock` is the board``

### `ww::expand::material_refusal`

ADR-0076: a `substance=` / `tempered=` that is malformed, or a feature the material cannot take (a cut in tempered glass). Message: `a material cannot be made or cut that way`

### `ww::expand::missing_stock`

Message: `` {what} has no `stock` ``

Help: ``every part is cut from a board: `stock <material> <x> <y> <z> [grain]` (ADR-0007)``

### `ww::expand::not_a_number`

Message: `` `{what}` is {found}, not a number ``

### `ww::expand::not_a_part`

Message: `` `{part}` is a sub-assembly, not a part ``

Help: `a join mates two boards; name a part inside the sub-assembly from within it`

### `ww::expand::not_text`

RFC-0122's `finish=`: a genuine string literal needs the CEL single-quote escape hatch (ADR-0004) — `finish "'danish oil'"`, not `finish "danish oil"`, which CEL reads as an (unknown) identifier. Message: `` `{what}` is {found}, not text ``

Help: ``a genuine string literal is a CEL expression that evaluates to one: `finish "'danish oil'"`, not `finish "danish oil"` (ADR-0004)``

### `ww::expand::one_sided_join`

`join joint=X male=Part@Mark` with no `female=`, for a joinery that joins two parts (RFC-0128, ADR-0030): only a `hardware_mount` mounts onto one. Message: `` `join joint={joint}` names no `female=`, but a `{kind}` joint joins two parts ``

Help: ``only a `hardware_mount` joinery may be fired with `male=` alone (one item on one part)``

### `ww::expand::skeleton`

ADR-0081: a malformed `locus`, a `part … in=` naming an unresolved locus, a `trimmed_by { … }`/`outline` that cannot be reconciled with its carrier, a `joints { rule … }` that is malformed or ambiguous, or a structural contact `joints {}` does not cover. One catch-all variant, like `BadStock`: the message and help carry the specifics, which differ by case far more than they repeat. Message: `{message}`

Help: `{help}`

### `ww::expand::stray_argument`

A bare word where only `x=`/`y=`/`z=` belong: nearly always a second node written on the same line (`pos x=1 mirror x=2`), which KDL reads as one `pos` carrying the argument `mirror` and a repeated `x=`. Refused rather than silently misplaced. Message: `` `{node}` takes no bare argument, but `{found}` was written after it ``

Help: `{help}`

### `ww::expand::too_deep`

`Limits::max_depth`, counted in assemblies: a root, every `assembly` nested inside it, and every sub-assembly placed with `instance=`, all the way down. Diagnosed before anything is placed, and before the check itself recurses past the limit. Message: `assemblies nest more than {limit} deep`

Help: ``counting nested `assembly` blocks and sub-assemblies placed with `instance=`, a part may sit at most {limit} assemblies deep (docs/KDL_AUTHORING.md §13)``

### `ww::expand::too_many_parts`

`Limits::max_parts`, counted with every sub-assembly instance multiplied out. Diagnosed from per-assembly counts, before any part is placed. Message: `the project places {total} parts; the limit is {limit}`

Help: ``a project may expand to at most {limit} parts, counting every part each `instance=` of a sub-assembly places (docs/KDL_AUTHORING.md §13)``

### `ww::expand::unexpected_node`

Message: `` `{node}` is not valid here ``

Help: `{expected}`

### `ww::expand::unexpected_property`

Message: `` `{node}` does not take `{key}` ``

Help: `{expected}`

### `ww::expand::unknown_anchor`

Message: `` `{name}` is not one of the standard anchors ``

Help: ``D6's anchors are top_/bottom_ followed by sw, se, ne, nw, n, s, e, w or center, plus `center` for the body centre``

### `ww::expand::unknown_hardware`

`hardware_use <Name>` naming no `hardware` catalogue item (ADR-0017). Message: `` `{name}` is not a declared hardware item ``

Help: `{help}`

### `ww::expand::unknown_instance`

Message: `` `{name}` is not a component or a top-level assembly ``

Help: `` `instance=` names a `component`, or an `assembly` declared at the top level ``

### `ww::expand::unknown_interpolation`

Message: `` `{name}`, interpolated in `{text}`, names nothing in scope ``

Help: `` `{…}`/`<…>` interpolates a name visible where the id is written — usually a `repeat` variable. It is a name, not an expression ``

### `ww::expand::unknown_joint`

Message: `` `{name}` is not a declared joinery ``

Help: `{help}`

### `ww::expand::unknown_mark`

Message: `` part `{part}` has no mark `{mark}` ``

Help: `{help}`

### `ww::expand::unknown_material`

Message: `` `{name}` is not a declared material ``

Help: `{help}`

### `ww::expand::unknown_part`

Message: `` no part `{part}` for `{part}@{mark}` ``

Help: `{help}`

## Stage `joinery`

### `ww::joinery::not_yet_synthesized`

Reported by the compiler with a message built at run time.

## Stage `lattice`

### `ww::lattice::degenerate_grid`

Message: `` `lattice rows={rows} cols={cols}` needs both at least 1 ``

Help: `` `rows`/`cols` count compartments (§6.8): at least one of each ``

### `ww::lattice::missing_prop`

Message: `` `lattice` has no `{key}=` ``

Help: `` `lattice` needs `rows=`, `cols=`, `bar_th=`, `wall_height=`, `fit_clearance=`, `border_th=`, `inner_width=` and `inner_length=`, all literal numbers ``

### `ww::lattice::non_literal_prop`

Message: `` `lattice`'s `{key}=` must be a literal number, not a CEL expression ``

Help: `the grid's own shape (how many parts/joins exist) is decided before any CEL expression is evaluated (docs/superpowers/plans/2026-10-29-drawer-dividers-698-plan.md): write the resolved number directly`

### `ww::lattice::omit_out_of_range`

Message: `` `omit {form}` names a grid line outside the lattice ``

Help: `` a single-coordinate `omit x= y=` needs `x` in `1..cols-1` and `y` in `1..rows`; a region `omit from_x= from_y= to_x= to_y=` needs its bounds inside `0..cols` and `0..rows` ``

## Stage `length`

### `ww::length::ambiguous`

Reported by the compiler with a message built at run time.

### `ww::length::bad_fraction`

Reported by the compiler with a message built at run time.

### `ww::length::bad_number`

Reported by the compiler with a message built at run time.

### `ww::length::bad_unit`

Reported by the compiler with a message built at run time.

### `ww::length::empty`

Reported by the compiler with a message built at run time.

### `ww::length::out_of_range`

Reported by the compiler with a message built at run time.

### `ww::length::zero_denominator`

Reported by the compiler with a message built at run time.

## Stage `link`

### `ww::link::inverse`

Reported by the compiler with a message built at run time.

## Stage `lower`

### `ww::lower::wasteful_stock`

Reported by the compiler with a message built at run time.

## Stage `override`

### `ww::override::conflict`

Reported by the compiler with a message built at run time.

### `ww::override::derived`

Reported by the compiler with a message built at run time.

### `ww::override::inverse`

Reported by the compiler with a message built at run time.

### `ww::override::inverse_unknown`

Reported by the compiler with a message built at run time.

### `ww::override::length`

Reported by the compiler with a message built at run time.

### `ww::override::range`

Reported by the compiler with a message built at run time.

### `ww::override::step`

Reported by the compiler with a message built at run time.

### `ww::override::type`

Reported by the compiler with a message built at run time.

### `ww::override::unknown`

Reported by the compiler with a message built at run time.

### `ww::override::unresolved`

Reported by the compiler with a message built at run time.

## Stage `plan`

### `ww::plan::ambiguous_ref`

Message: `` `{name}` is ambiguous in this plan ``

Help: `{detail}`

### `ww::plan::cladding_of_edge`

Message: `` `of={name}` names an `edge`, not a `wall` ``

Help: ``an `edge` bounds a room without being a wall and has no face to clad``

### `ww::plan::cladding_of_shares_wall`

Message: `` `of={name}` names a `shares=`'d wall, which has no face of its own ``

Help: ``a `shares=`'d wall has no endpoints of its own to build a face from (RFC-0154's own adversarial review, item 5, left open) — clad the wall it shares, or the other room's own side of it, if that has an exterior face``

### `ww::plan::cladding_of_unknown_wall`

Message: `` `of={name}` names no wall of this room ``

Help: `` `cladding`'s `of=` must name one of this room's own `wall` sides ``

### `ww::plan::clearance_outside_room`

Reported by the compiler with a message built at run time.

### `ww::plan::clearance_overlap`

Reported by the compiler with a message built at run time.

### `ww::plan::clearance_too_close`

Reported by the compiler with a message built at run time.

### `ww::plan::cover_of_forward_layer`

Message: `` `of={name}` names a layer declared later (or self) in this room ``

Help: `` `cover`/`upstand`'s `of=` must name a layer already declared earlier in the same room (#656 §2.3) ``

### `ww::plan::cover_of_wrong_body`

Message: `` `of={name}` names a `{kind}`-bodied layer, which `cover`/`upstand` cannot derive a footprint from ``

Help: `` `cover`/`upstand` derive a footprint from a layer's placed geometry, which only a `path`-bodied layer has (#656 §2.3): `of=` must name a `path` layer, not another `cover`, `upstand` or `roof` layer ``

### `ww::plan::direction_on_freestanding_path`

Message: `` `direction=` on a `path` whose `{node}` leaves the room perimeter ``

Help: `` `direction=` has no meaning once a `path` leaves the perimeter — remove it, or address `{node}` with `corner=` instead (final consolidation §1/§6.1) ``

### `ww::plan::duplicate_interface`

Message: ``a second `interface` on this assembly``

Help: `` an assembly may declare at most one `interface` ``

### `ww::plan::duplicate_name`

Message: `` `{name}` is declared more than once in this plan ``

Help: `walls, openings, features, obstacles, runs, places and marks share one namespace per plan`

### `ww::plan::duplicate_plan`

Message: ``a second `plan`/`site` in one project``

Help: ``at most one `plan`/`site` per project (ADR-0060 Q2); a project needing two unrelated plans is two files``

### `ww::plan::empty_layer`

Message: `` layer `{layer}` has no `path` ``

Help: ``a `layer` needs at least one `path` (or, once Task 5 lands, a `roof`/`cover`/`upstand`)``

### `ww::plan::exclude_forward_instance`

Message: `` `exclude {name}` names an instance placed in this layer or a later one ``

Help: `` `exclude <InstanceId>` may only name an instance placed in an *earlier* layer of the same room (final consolidation §3.2) ``

### `ww::plan::expose_unknown`

Message: `` `expose {name}` names neither a param of `{design}` nor a project param of its file ``

### `ww::plan::fill_without_stretch`

Message: `` `fill` names `{design}`, which has no `stretch` param ``

Help: ``a `fill` solves a `stretch` param to close the run; expose and `stretch` one on `{design}`'s interface``

### `ww::plan::footprint_outside`

Message: `` `{design}`'s built solids ({built}) reach outside its declared footprint ({footprint}) ``

Help: `the footprint is an envelope (ADR-0060 §4.6, Q8): enlarge it, or bring the build inside it`

### `ww::plan::forward_layer_ref`

`height=`/`offset=`/`anchor=` naming `<Layer>_bottom`/`_top`/`_height` of a layer declared later (or of itself) in the same room (#653 §6.4, #647/#657 Task 3). Named `ww::plan::forward_layer_ref`, not `ww::eval::*`, deliberately: this diagnostic is part of the `ww::plan::*` family the rest of the layer/path grammar's diagnostics belong to (`crates/ww-dsl/src/error.rs`), even though the check itself can only run here — it needs `Program::references().variables()`, which only this crate's `cel` dependency provides (`ww-dsl` has none). Message: `` `{expr}` names a layer declared later in this room ``

Help: ``a `height=`/`offset=`/`anchor=` expression may only reference `<Layer>_bottom`/`_top`/`_height` of a layer declared earlier in the same room (#653 §6.4)``

### `ww::plan::forward_path_ref`

Message: `` `{name}` names a path declared later (or self) in this room ``

Help: `` `path_start=`/`path_end=`/`between=` naming `{name}_start`/`{name}_end` need that path declared earlier in the same room (first synthesis §3.1) ``

### `ww::plan::layer_overflow`

A sequentially-packed segment (module doc: the independently-packed run `exclude` splits a path into) sums to more than the path's own available length — a wall or room-perimeter run, for a segment this module fully resolved (module doc's own "stand-in, not final" discipline: a path mixing sequential packing with an unresolved mode such as `anchor=` never reaches this check at all, because `lower_path` bails the whole path to `None` before any segment is measured; this variant only ever sees a segment this module is fully confident in). Message: `{segment} packs to {packed_mm:.1}mm, {overshoot_mm:.1}mm past its {available_mm:.1}mm available length`

### `ww::plan::mark_shadows_face`

Message: `` `mark {name}` shadows a face name ``

Help: ``a mark may not be named `front`, `back`, `left`, `right`, `top` or `bottom`: those name faces``

### `ww::plan::missing_roof`

Message: `` room `{room}` has `layer`s but no `Roof` layer and no `roof_of` ``

Help: ``say so explicitly: give this room its own `layer Roof` with a `roof` child (Task 5), or `roof_of room=<RoomName>` naming the room whose roof covers it (final consolidation §3.4) — never an implicit default``

### `ww::plan::missing_wall_thickness`

Message: `` wall `{wall}` bears room `{room}`'s shed roof but declares no `thickness=` ``

Help: ``the roof's bearing/seat math (2026-10-25-roof-truss-lowering-683.md) needs this wall's thickness — add `thickness=<CEL>` to `wall {wall}` (2026-10-25-wall-thickness-683.md §2); only a shed roof's `high_side=` is checked here, a gable roof's bearing walls are not yet determined structurally and so are not enforced by this diagnostic``

### `ww::plan::mount_needs_wall`

Message: `` `{design}` mounts to a wall but `{place}` has neither a `run` nor an `against` ``

Help: `` a wall-mounted design needs a wall to hang from: put it in a `run`, or give it an `against <Wall>` ``

### `ww::plan::no_footprint`

Message: `` `interface` has no `footprint` ``

Help: ``a placeable design needs `footprint w= d= h=` (ADR-0060 §4.6)``

### `ww::plan::no_interface`

Message: `` `{design}` has no `interface` ``

Help: ``only a design with an `interface` can be placed (ADR-0060 §4.6)``

### `ww::plan::no_layer_height`

Message: `` layer `{layer}` has no `height=` ``

Help: `` `height=` is required on every `layer` (#653 §3): it is the layer's bottom elevation ``

### `ww::plan::not_abuttable`

Message: `` `{design}` has no `abut` faces, so it cannot sit mid-run ``

Help: ``declare `abut left right` (or whichever faces touch a neighbour) on `{design}`'s interface``

### `ww::plan::not_exposed`

Message: `` `{key}` overrides a param `{design}` does not expose ``

Help: `` only params an `interface`'s `expose` lists may be overridden on a `place` ``

### `ww::plan::op`

Reported by the compiler with a message built at run time.

### `ww::plan::opening_off_wall`

Message: `` `{name}` sits off the end of wall `{wall}` ``

Help: `` `at=` plus `width=` must fit between the wall's two ends ``

### `ww::plan::packing_prop_outside_path`

Message: `` `{key}=` on a `place` that is not inside a `path` ``

Help: `` `offset_pin=`, `offset=`, `corner=`, `between=` and `anchor=` only mean something on a `place` inside a `path` (#653 §5); a `run` has its own sequential/`fill` packing ``

### `ww::plan::path_endpoint_ambiguous`

Message: `` more than one addressing form on this `{node}` ``

Help: ``a `path`'s `start`/`end` takes exactly one of `corner=`, `x=`/`y=`, or `path_start=`/`path_end=` (final consolidation §6.1)``

### `ww::plan::path_endpoint_incomplete_xy`

Message: `` `{node}` has `{given}=` without its pair ``

Help: `` `x=` and `y=` on a `path`'s `start`/`end` must both be given (final consolidation §1/§6.1) ``

### `ww::plan::path_endpoint_off_wall`

Message: `` `{node}` is not on `wall={wall}` ``

Help: ``on a `wall=`-scoped `path`, `start`/`end` are interpreted as the two ends of that one wall (final consolidation §4): a `corner=` must be one of that wall's own two ends, and an `x=`/`y=` point must lie on it``

### `ww::plan::path_wall_unknown`

Message: `` `wall={wall}` names no wall or edge of this room ``

Help: ``a `path`'s `wall=` must name one of the room's own `wall`/`edge` sides (final consolidation §4)``

### `ww::plan::plan_in_import`

Message: `` `{path}` declares a `plan`, which an imported file cannot do ``

Help: ``plans are not importable (ADR-0060 Q15); move the `place` of this design into the importing file's own plan``

### `ww::plan::reserved_param`

Message: `` `expose {name}` collides with the reserved override `{reserved}` ``

Help: `` `label`, `finish`, `mirror` and `role` are reserved on `place`; expose a different name ``

### `ww::plan::rev`

Reported by the compiler with a message built at run time.

### `ww::plan::roof_missing_high_side`

Message: `` `roof kind=shed` has no `high_side=` ``

Help: `` `high_side=<WallName>` is required for `kind=shed` (#653 §8): it names the wall the ridge runs along ``

### `ww::plan::roof_missing_ridge_axis`

Message: `` `roof kind=gable` has no `ridge=` ``

Help: `` `ridge=along_x|along_y` is required for `kind=gable` (#653 §8) ``

### `ww::plan::roof_mixed_with_path`

Message: `` a `layer` mixes `{a}` with `{b}` ``

Help: ``a `layer` uses exactly one mechanism: `path`, `roof`, `cover` or `upstand` — never more than one``

### `ww::plan::roof_pitch_conflict`

Message: `` `roof` gives both or neither of `pitch=` and `rise_in_12=` ``

Help: ``exactly one of `pitch=<deg>` or `rise_in_12=<ratio>` is required``

### `ww::plan::roof_ref_not_shared_room`

Message: `` `roof_of room={name}` names a room this one does not share a wall with ``

Help: `` `roof_of=` may only name a room this one is wall-connected to via `shares=` (final consolidation §3.4) ``

### `ww::plan::room_not_closed`

Message: ``room `{room}` is not a closed polygon``

Help: `` the last `wall`/`edge` must return to `start x= y=` ``

### `ww::plan::room_self_intersecting`

Message: ``room `{room}`'s walls cross themselves``

Help: `list a room's sides in order round the room, either direction, with no crossing`

### `ww::plan::shares_dimension_mismatch`

Message: `` `shares` wall `{name}` is a different length than `{room}.{wall}` ``

Help: ``without `span=`, a `shares`'d wall must be the same length as the wall it shares``

### `ww::plan::shares_opening_mismatch`

Message: `` `shared_with`'s restated `{key}=` disagrees with `{room}.{opening}`'s own value ``

Help: `` `at=`/`width=` are optional on a `shared_with` opening (derived from the counterpart); restated anyway, they must agree (final consolidation §3.3) ``

### `ww::plan::shares_span_out_of_range`

Message: `` `shares`'s `span` exceeds `{room}.{wall}`'s own length ``

Help: `` `span`'s `at=`/`length=` must fit inside the named wall's own length ``

### `ww::plan::shares_wall_has_coordinates`

Message: `` a `shares`'d wall also gives its own `x=`/`y=`/`thickness=` ``

Help: `` a `shares`'d wall's position and thickness are both derived from the named wall, never independently stated (final consolidation §3.1; thickness per 2026-10-25-wall-thickness-683.md §3) — remove `x=`/`y=`/`thickness=`, or remove `shares` ``

### `ww::plan::span_self_reference`

Message: `` `between=` names the same anchor twice ``

Help: `` `between=<AnchorA> <AnchorB>` needs two distinct anchors ``

### `ww::plan::standard_out_of_range`

Message: `` `standard` value for `{name}` is outside `{min}..{max}` ``

### `ww::plan::unknown_design`

Message: `` `{design}` names no top-level assembly in scope ``

Help: ``a `place`/`fill`/`needs design=` names a top-level assembly of this file or an imported one``

### `ww::plan::unknown_ref`

Message: `` `{name}` names nothing in this plan ``

Help: `{available}`

## Stage `preset`

### `ww::preset::derived`

Reported by the compiler with a message built at run time.

### `ww::preset::duplicate`

Message: ``preset `{name}` is declared more than once in this project``

Help: `two presets may not share a name; rename one of them`

### `ww::preset::range`

Reported by the compiler with a message built at run time.

### `ww::preset::step`

Reported by the compiler with a message built at run time.

### `ww::preset::type`

Reported by the compiler with a message built at run time.

### `ww::preset::unknown`

Reported by the compiler with a message built at run time.

