# Rules and fixes

The studio checks every draft against rules R1–R10: on every `create_draft` and `update_draft` (the quick check), and again with `validate_draft` (the full check). Every problem names its rule, a code, the path in the design where it is, the settings involved and a fix. The fix's `say` names the field or setting to change and the values it may take; it has a `patch` (JSON Patch operations) only when the change is certain. Fix errors before the next change; read warnings and act on them too.

## The rules, as `get_rules` gives them

```text
Slatfinity design rules. The studio checks them on every update_draft and again before publishing. Each problem it reports names its rule (R1–R10), a code, the path in the design, and a fix; the fixes below are the same words, written for an example path.

Studio rules
- Pick a type from the vocabulary. You can't add types; use the closest one.
- Sizes and options are presets of one design. Make a new design only for a different shape or mechanism. If the studio warns that a draft is close to an existing design, make a preset or a remix instead.
- The studio calculates the load per wall for stated print assumptions: information for improving the design, not a guarantee and never a refusal. The catalog's class is calculated (`moderate` from 5 kg at the defaults and every preset, else `light`); a design's own `load` is an optional note.
- Research may set sizes and ideas. Never copy another model's shape. If a licensed model inspired the design, name it in `sources` as `inspired-by`.
- Only mount bricks touch the wall. The core (mounts, joints, locks, fit presets, wall data) can't be changed here.

R1 Format. The draft matches the schema, uses only known bricks at pinned major versions, and contains only expressions.
   How it is checked: Bricks are pinned as id@major (list_bricks shows them). Values are numbers, booleans, strings, or formulas that start with "=". A draft is at most 256 KiB of JSON, nested at most 40 levels deep, with at most 20,000 values.
   Problems and fixes:
   - schema: Fix the design's top level. A design is one JSON object with design, version, title, description, tags, license, free, family, type, holds, where, works_with, needs, materials, settings, uses and parts; load, shapes, presets, expose, print_jobs, batch, checks, sources and load_point are optional. Remove any other key. Change shapes.<shape> so it matches the design format (the design JSON Schema has every field's shape).
   - too-deep: Remove the deeply nested value: no value may nest more than 40 levels, and no value may contain itself. Designs never need deep nesting.
   - not-json: Send the design as plain JSON: objects, arrays, strings, finite numbers, true, false and null only.
   - expr-parse: Rewrite the formula at settings.<name>.default. A formula starts with "=" and reads settings.<name>, wall.<field> (pitch, slot_opening, lip_thickness, undercut_depth, panel_depth), fit.<field> (dovetail, print_in_place, bore, axial), setup.bed.x, setup.bed.y and <instance>.<readout>; it uses numbers, "text" in quotes, true and false, + - * / % ^, comparisons < <= > >= == != (not chained), && || !, the choice a ? b : c, parentheses, and the functions min, max, clamp, round, floor, ceil, abs, sqrt, sin, cos, tan, asin, acos, atan2, lerp.
   - unknown-ref: Change shapes.<shape>.from.0 to name something that exists: a shape under "shapes", "instance.part" for a brick under "uses", a setting under "settings", or a setting the brick's manifest lists.
   - unknown-brick: Set uses.<instance>.brick to a known brick written "id@major", like "cam-lock-mount@1".
   - unknown-part: Set parts.<part>.from.0 to a part that exists: a key of "parts", or "instance.part" for a part a brick makes.
   - cyclic-shape: Break the loop at shapes.<shape>: a shape can't use itself, directly or through other shapes. Point one of its refs at a different shape or at a brick part.
   - bad-value: Set uses.<instance>.settings.<name> to a value of the setting's type: a number for length, angle, number and count; true or false for a toggle; one of the option values for a choice; text in quotes for text, font and list; or a formula starting with "=".
   - more-issues: Fix the issues listed, then check the design again: the rest are listed once these are gone.
   - missing-field: Add <field>; the design JSON Schema gives its shape.
R2 Mounting. Any part that engages the wall does so through a mount brick (cam-lock-mount, hook-arm, cradle, shelf-bracket, multi-slot-lock, clip-label, label-clip, press-in-label, press-in-holder, calibration, cam-tuning and top-wedge). Freeform shapes cannot intersect the wall volume. This is checked geometrically against the wall model.
   How it is checked: Everything behind the slat face belongs to the wall and its slots. On every claimed wall, each part minus the mount bricks' own parts must stay in front of the face. Moving a mount brick's part makes it your geometry.
   Problems come from the build checks; each names the part, wall or state and what to change.
R3 Settings. Every setting has a range, unit, label and level. There are at most 6 simple settings. The defaults must build.
   How it is checked: Lengths use "mm" and angles "°"; numbers need "%" or "". Exposed brick settings with level simple count toward the 6.
   Problems and fixes:
   - bad-setting: Make settings.<name> a setting the format knows: type is length, angle, number, count, toggle, choice, text, font or list, with that type's fields (a length has default, min, max, step, unit "mm", label and level).
   - bad-range: Set settings.<name>.min below settings.<name>.max, both finite numbers, so people get a range to choose from.
   - default-out-of-range: Set settings.<name>.default to a number between settings.<name>.min and settings.<name>.max.
   - bad-default: Set settings.<name>.default to a value of the setting's type: a number for length, angle, number and count; true or false for a toggle; one of the option values for a choice; text in quotes for text, font and list; or a formula starting with "=".
   - bad-unit: Set settings.<name>.unit to "mm": designs store lengths in millimetres. If it is a share or a ratio, make it type "number" with unit "%" or "". Set settings.<angle>.unit to "°": designs store angles in degrees.
   - no-unit: Set settings.<name>.unit to "%" for a percentage or "" for a plain number.
   - bad-label: Set settings.<name>.label to the short name people see, 1–60 characters, like "Reach".
   - bad-step: Set settings.<name>.step to a number above 0, like 0.5 for a length in mm.
   - bad-level: Set settings.<name>.level to "simple", "advanced" or "expert"; at most 6 settings can be "simple".
   - too-many-simple: Set the level of some settings to "advanced" (settings.<name>.level, or level on an expose entry) so at most 6 are "simple". Keep the ones people change most.
   - bad-expose: Fix expose.0: expose lists "instance.setting" names of settings the brick's manifest has, each once, and none already fixed under uses.<instance>.settings; a min or max only narrows a number setting's range, keeping the brick's default inside it.
   - cyclic-setting: Break the loop at settings.<name>.default: a default can't read itself, directly or through other settings. Give one of them a number instead of a formula.
   - value-clamped: Set uses.<instance>.settings.<name> inside its setting's range (get_brick lists a brick's ranges), or use a brick or setting whose range reaches the value you need: outside it, the value is held to the nearest end.
R4 Every wall. The design builds with zero errors on each claimed wall, at the defaults and at the min/max corners of every simple setting (one at a time), and it can go on: tipped in and rolled down (or pushed straight in) without hitting the wall.
   How it is checked: A design claims every enabled wall preset that all of its bricks accept. update_draft builds the defaults on one wall; validate_draft (full) builds every wall, every brick state and every simple corner. A hooked mount goes on tipped about 50–55° and rolled down, so anything above the slot's top edge near the wall swings into the slat above: keep it low, or set it out from the face by the tip's slope (install-blocked says how much).
   Problems come from the build checks; each names the part, wall or state and what to change.
R5 Solid parts. Every part is manifold, fits the bed (256 mm reference) and has a minimum wall of 1.2 mm.
   How it is checked: Parts are sliced across 13 directions and each slice is opened by 0.59 mm, so material under 1.18 mm across disappears and a wall of 1.2 mm survives. A lost piece of over 2 mm² that is lost again 0.35 mm either side, added by your shapes or left by your cutters, fails. Thin features inside brick parts are the brick's own, reviewed design.
   Problems come from the build checks; each names the part, wall or state and what to change.
R6 Fits. Joint clearances come from fit.*. A literal clearance below 0.5 mm in a design is rejected.
   How it is checked: Settings and brick bindings whose names contain clear, fit or gap, and every profile offset, must start from fit.dovetail, fit.print_in_place, fit.bore and fit.axial (a tweak like =fit.dovetail + 0.05 is fine), or be at least 0.5 mm.
   Problems and fixes:
   - literal-clearance: Remove uses.<instance>.settings.<name> to keep the brick's own default, which the brick chose for this joint. Or set it to a number of at least 0.5 mm.
   - possible-clearance: If the small number added or taken at settings.<name>.default is a clearance between parts, start the formula from the printer fit instead, like "=fit.dovetail + …" (fit terms: "=fit.dovetail" (0.08–0.15 mm), "=fit.print_in_place" (0.25–0.4 mm), "=fit.bore" (0.12–0.2 mm) or "=fit.axial" (0.25–0.4 mm)). If it isn't a clearance (an overlap, a rounding), leave it.
R7 Materials. Every part has a material slot and a print orientation. There are 1–4 slots.
   How it is checked: Set print.orient on each part with design shapes in it, or print.in_place_with another part. A part made only of brick parts may leave it out: the bricks then decide.
   Problems and fixes:
   - bad-materials: Give materials 1–4 slots, each { "label": "Body", "default": "#RRGGBB" }.
   - unknown-material: Set parts.<part>.material to a key of "materials".
   - too-many-materials: Remove material slots until there are at most 4; parts can share a slot. Then point each part's material at a remaining slot.
   - no-orient: Set parts.<part>.print.orient to "as-modelled", "on-side", "flat" or "upright", or set parts.<part>.print.in_place_with to the part it prints inside.
   - unknown-in-place: Set parts.<part>.print.in_place_with to another part of this design, or remove it and set print.orient to "as-modelled", "on-side", "flat" or "upright".
   - in-place-loop: Break the in_place_with loop: give one part of it print.orient ("as-modelled", "on-side", "flat" or "upright") instead of in_place_with, so that part owns the print and the others print in place with it.
   - bad-print: Make parts.<part>.print { "orient": "as-modelled", "on-side", "flat" or "upright" } or { "in_place_with": "<another part>" }.
R8 Motion. Brick motion sweeps without collisions in every state.
   How it is checked: Moving brick parts (keys and their levers) are turned through their whole travel against every other part, in steps of at most 1.5° and 0.5 mm of travel; an overlap of over 0.01 mm³ at any step fails.
   Problems come from the build checks; each names the part, wall or state and what to change.
R9 Listing. The design has a title, a description of at least 40 characters, 1–8 tags, a licence from the allowed list, and a listing from the catalog vocabulary.
   How it is checked: Licences: CC-BY-4.0, CC-BY-SA-4.0 and CC0-1.0 (CC-BY-4.0 is the default). Tags are lowercase words joined by hyphens, like garden-tools. The listing is family, type, holds, where, works_with and needs; load is an optional note (the catalog's class is calculated, and a load finding is a warning, never an error). A description over 60 words is a warning: 25–45 reads best.
   Problems and fixes:
   - bad-load-point: Set load_point to { "anchor": "instance.anchor", "offset": [x, y, z] }: an anchor of a brick under uses, and an offset in mm with each number from -300 to 300.
   - unknown-anchor: Set load_point.anchor to "instance.anchor": a brick instance under uses and an anchor its manifest lists. Add a brick with anchors if there is none.
   - bad-seat: Set listings.<part>.seat so the seat matches the format: { kind: floor | over | channel | grip | onto, item?: a hold id of the part, at: { anchor?: "instance.anchor", offset: [x, y, z] }, axis?: x | y | z, size: { diameter | width | depth | height | length | thickness: mm above 0 }, tilt?, turn?, squeeze?: mm, 0 or more, copies?: { count: 2–12 or a formula, step: [x, y, z] }, pair?: { spacing: mm above 0 }, none?: [preset] }. Each value is mm or degrees, or an "=formula".
   - seat-anchor: Set listings.<part>.seat.at.anchor to "instance.anchor": a brick instance under uses and an anchor its manifest lists. Add a brick with anchors if there is none. Or leave the anchor out, and the offset is in the design's own coordinates.
   - seat-holds: Add the item the seat places to the part's holds (and the design's holds), or set item to one of them, or remove the seat.
   - seat-none: List in none only this part's own sizes that can't show the item, and never all of them; if no size can show it, remove the seat.
   - seat-field: Remove the field the seat's kind doesn't use (squeeze is for a grip seat, tilt for a floor, over or channel seat; a floor seat takes no axis, and an over seat's axis is x or z), and name the measure its kind is judged by: a grip's diameter, a floor's diameter or its width and depth.
   - seat-expression: Rewrite the formula at listings.<part>.seat.size.diameter so it reads names a shape can use: settings.<name>, wall.<field>, fit.<field>, setup.bed.x, setup.bed.y or <instance>.<readout>.
   - seat-copies: Make the copies count come to a whole number from 1 to 12 at the defaults and at every size the part shows, or list a size it can't be in none.
   - bad-title: Set title to a name of 1–80 characters on one line, with no tabs or control characters, like "Bin hook".
   - short-description: Write a description of at least 40 characters: what it holds and where.
   - long-description: Cut the description to 25–45 words: what it holds and where (a verb first where it can, within 90 characters), then what makes it different, then the must-knows: PETG or ASA for a part that carries a load, and any safe load below 2 kg. Leave out the lock, the walls, calculated loads and print settings: the part page shows them.
   - bad-description: Set description to plain text of at most 4000 characters.
   - tag-count: Give tags 1–8 entries, like ["hooks", "garage"].
   - bad-tags: Set tags to a list of 1–8 short lowercase words, like ["hooks", "garage"].
   - bad-tag: Change tags.0 to lowercase letters and digits joined by single hyphens, at most 30 characters, like "garden-tools".
   - bad-license: Set license to one of: CC-BY-4.0, CC-BY-SA-4.0, CC0-1.0.
   - unknown-family: Set family to one of: "hooks", "locks", "labels", "tool-storage", "bins", "shelves", "mounts", "fit-finish", "rods", "devices", "sports" or "garden".
   - unknown-type: Use the closest type in the vocabulary. New types are proposed once a month. Set family first ("hooks", "locks", "labels", "tool-storage", "bins", "shelves", "mounts", "fit-finish", "rods", "devices", "sports" or "garden"), then type to one of that family's types.
   - bad-listing: Set holds to a list of at most 8 different ids, each one of "broom-mop", "rake-shovel", "drill-battery", "drill", "extension-cord", "garden-hose", "spray-can", "caulk-tube", "screwdrivers", "pliers", "bits-hex-keys", "tape-measure", "paper-towel", "filament-spool", "tape-roll", "helmet", "cap", "safety-glasses", "power-strip", "cables", "phone-tablet", "headphones", "labels", "bins-tubs", "small-parts", "jacket-bag", "shelf-board", "fishing-rod", "ball", "trash-bags", "gloves-rags", "steel-hooks", "spray-bottle", "paint-can" or "wire-basket". Use [] if none fit. Set where to 1–3 different ids, each one of "garage", "workshop" or "home-office". Set load to "light" (under 2 kg) or "moderate" (2–5 kg). Set works_with to a list of at most 8 different ids, each one of "ryobi-one-plus", "dewalt-20v", "milwaukee-m18", "ego-56v", "milwaukee-packout", "gridfinity", "ikea-skadis", "multiconnect", "opengrid" or "steel-hooks". Use [] if it isn't made for a tool system. Set needs to one id (not a list): "none", "dowel", "magnet-strip" or "metal-nut". Set listings to { "<part id>": { "name": "…", "holds": [hold ids], "sizes": [preset names] } }, 1–8 parts. A part id is lower-case words joined by single hyphens, with a letter, at most 60 characters, like "spray-can-cup". name is 1–80 characters; holds is at most 8 of the design's holds; sizes names presets, or { "preset", "label" (at most 40 characters), "note" (at most 120) }, and [] makes the part the design at its defaults; goes_with is at most 5 { "part": "<part id>", "why": "…" }. No other keys.
   - type-family: Set family to the family of the design's type.
   - load-over-type: Set the load note to the type's listing, or leave it: the listing uses the calculated class. For a heavier listing, pick a type made for heavier things.
   - duplicate-facet: Remove the repeated "id" at holds.1.
   - preset-id-clash: Rename presets.<preset> so the two preset names differ in more than case, spaces and punctuation (their links lower-case them and join words with "-").
   - preset-holds: Each key of preset_holds names one of the design's presets, and lists only items in the design's holds. Take an item the design doesn't hold off the preset (or add it to holds), and rename or remove a key that isn't a preset.
   - deprecated-preset-holds: Replace preset_holds with listings: each group of presets that hold the same things becomes one part. The patch names the first part by the design's title and each other part by its first preset; when no preset holds some of the design's items, it adds a part at the defaults (sizes: []) that holds them all, as the design did. Rename the parts the way people would look for them.
   - presets-without-listings: Add listings. One listing with every preset is right when every preset holds all of the design's items; otherwise split them.
   - listing-holds: Take listings.<part>.holds.0 off the part, or add the item to the design's holds if the design is for it.
   - listing-size: Write each size as the name of one of the design's presets, exactly as under presets. A design with no presets has one part, with sizes: [].
   - size-twice: Take listings.<part>.sizes.1 off this part: the preset is already a size of another part, and a preset belongs to one part.
   - size-unlisted: Make each preset a size of one part. The patch adds the presets not listed yet to the sizes of the first part that has sizes; move each to the part it belongs to, or give them a listing of their own.
   - defaults-twice: Only one part can be the design at its defaults (sizes: []). Give listings.<part>.sizes the presets this part is made in (add a preset if it has none), or fold the part into the one at the defaults.
   - listing-holds-union: Set the design's holds to the items its parts hold. The patch takes off the items no part holds; or add the missing item to the part that holds it.
   - goes-with-self: Remove listings.<part>.goes_with.0: goes_with names other parts used with this one, never the part itself.
   - deprecated-category: Remove category: family and type replace it.
   - bad-source: Make each source { "url": "https://…", "kind": "dimensions", "inspired-by", "competitor" or "standard", "note": at most 200 characters }, with at most 10 sources. The url must be a plain https:// link: printable ASCII with no spaces or backslashes, a named host (not an IP address; a Unicode host in its xn-- form) and no user:password@ part.
   - item-source: Replace sources.0.item with "url": an https:// link to a page that gives the item's sizes.
R10 Limits. Build ≤ 120M units of engine work, ≤ 2M triangles per part.
   How it is checked: Every build's engine work is counted, its rules included: each engine call, the triangles and outline points it reads and makes, the pairs of triangles a boolean has to test where solids cross, and the thin-wall check's own work. Shapes that cross at one place (bars in a star, a dense lattice) cost far more than their size. The count is the same on every machine. A build is stopped as soon as its count passes the limit, and the finding names the shapes and checks that did most of the work. Every part's triangles are counted too; a design over either limit fails with R10. Re-checking content that hasn't changed reuses its report.
   Problems and fixes:
   - too-large: Make the design smaller: at most 256 KiB of JSON and 20,000 values. Shorten long text (description, help, notes) and use settings, formulas and repeat instead of copies.
   - too-complex: A build went quiet past its time limit, the geometry engine failed, or a build ran out of memory; it was stopped and nothing passed. Make the design simpler: fewer copies, fewer or simpler shapes, a coarser lattice.
   - too-slow: The whole check (or inspect) ran past its budget, however many builds finished; it was stopped and didn't pass. Make the design simpler: fewer copies, fewer or simpler shapes, a coarser lattice.
   - check-failed: The check couldn't run because of a problem on the studio's side (named in the message), not the design. Nothing to change in the design: run the check again. If it fails the same way again, report it with this message.

Style guide
- Start from a brick's example (get_brick returns one when the brick has one) and change one thing at a time. Read the change report update_draft returns after each change; call inspect_draft when you need to see it.
- Coordinates are millimetres and degrees. Hooks and brackets: x out of the wall (0 = the slat face), y up (0 = the hook seat), z across the width. Labels: x along the label, y up (0 = the slot floor), z out of the wall.
- Simple settings are what a person changes: width, reach, how many. Put tuning in advanced. Never expose a print-tested mechanism number as simple.
- Setting keys are snake_case; labels are plain words in sentence case, like "Arm reach".
- Every gap your design adds between two parts comes from fit.* (dovetail, print_in_place, bore, axial). Some bricks have fixed, print-tested gaps of their own, like the cam lock key's room in the slot: leave those alone.
- Orient parts so the load runs along the layers: hooks print on their side, plates flat and face up.
- Place brick parts with the brick's own settings and anchors. A brick part moved inside a shape counts as your geometry for R2 and R5.
- Descriptions are 25–45 words, plain and active, in at most three sentences. Sentence 1 says what it holds and where, starts with a verb where it can and fits in 90 characters, with no load figure: llms.txt, and a search snippet when a part has no holds or note, show only that sentence. Sentence 2 says what makes it different or how you use it. Sentence 3 gives the must-knows: always 'Print in PETG or ASA, not PLA.' for a part that carries a load or a spring, and any safe load below 2 kg; then at most one of a bought part, the one thing to measure, or a 'not for'. With those, up to 55 words.
- Leave the lock, the walls it fits, calculated loads, print settings and geometry details (chamfers, gussets) out of descriptions: the part page shows them. Over 60 words is a long-description warning.

Design file (YAML in the repo, JSON here)
- design (slug), version (semver), title, description, tags (1–8), license (CC-BY-4.0, CC-BY-SA-4.0, CC0-1.0), free
- the listing, from the catalog vocabulary: family (hooks, locks, labels, tool-storage, bins, shelves, mounts, fit-finish, rods, devices, sports, garden); type (one of that family's types; 68 in all); holds (0–8 ids); where (1–3 ids); load? (light or moderate: an optional note; the catalog's class is calculated); works_with (0–8 ids); needs (one id: none, dowel, magnet-strip, metal-nut)
- sources (0–10): { url (https), kind (dimensions, inspired-by, competitor, standard), note? }; load_point?: { anchor: "instance.anchor", offset: [x, y, z] }
- materials: 1–4 slots { label, default "#rrggbb" }
- settings: { type length|angle|number|count|toggle|choice|text|font|list, default, min, max, step, unit, label, level simple|advanced|expert, help?, group? }
- presets, expose ("instance.setting" or { from, level, label, group })
- listings?: { <part id>: { name, note?, holds, sizes, goes_with? } }: the parts the file makes (at most 8; a part id is lower-case words joined by hyphens with at least one letter, at most 60 characters, and never renamed once published). A part's holds are some of the design's holds, and together the parts hold exactly those; sizes are preset names or { preset, label?, note? } (every preset is a size of one part; [] is the design at its defaults); goes_with is { part, why } (at most 5); seat? is how the part holds its item, for the 3D view: { kind: floor|over|channel|grip|onto, item?, at: { anchor?, offset: [x, y, z] }, size: { measure: mm }, axis?, tilt?, turn?, squeeze?, copies?, pair?, none? }. Leave it out and the file makes one part, named by its title. preset_holds is retired
- uses: { instance: { brick: "id@major", settings: { key: value or "=formula" } } }
- shapes: profile + extrude/revolve, box, cylinder, union/subtract/intersect/hull of shape names or "instance.part", then mirror, rotate, move, attach (to "instance.anchor"), repeat
- parts: { name: { from: [...], material, print: { orient | in_place_with | infill } } }, print_jobs, batch, checks (print.infill: 15-100 %, only for a part that carries load, such as a peg; keys get the person's key infill)
Formulas start with "=": + - * / % ^, comparisons, && || !, a ? b : c; min max clamp round floor ceil abs sqrt sin cos tan asin acos atan2 lerp (degrees); names settings.*, wall.* (pitch, slot_opening, lip_thickness, undercut_depth, panel_depth), fit.* (dovetail, print_in_place, bore, axial), setup.bed.x, setup.bed.y, <instance>.<readout>
```

## Real errors and their fixes

These were made by running the rules on small sample designs, so the words are the studio's own.

**A hand-typed clearance (R6).** A design setting `dt_fit` with `default: 0.1` (a number, so it can't follow the printer) gives:

```json
{
  "rule": "R6",
  "code": "literal-clearance",
  "level": "error",
  "path": "settings.dt_fit.default",
  "message": "The clearance setting \"dt_fit\" is 0.1 mm whatever the printer fit. Clearances under 0.5 mm must come from the printer fit (fit.*), so they follow each person's printer.",
  "fix": {
    "say": "Use a fit.* value, so the clearance follows the person's printer fit. Set settings.dt_fit.default to \"=fit.dovetail\" (0.08–0.15 mm across the tight, standard and loose fits), plus a margin if the joint needs one. A fixed number must be at least 0.5 mm."
  }
}
```

The fix is a formula: `=fit.dovetail` as the default, so the gap follows each person's printer fit.

**A listing id the vocabulary doesn't have (R9).** A design with `holds: ["garden-gnome"]` gets:

```json
{
  "rule": "R9",
  "code": "bad-listing",
  "level": "error",
  "path": "holds.0",
  "message": "Invalid enum value. Expected 'broom-mop' | 'rake-shovel' | 'drill-battery' | 'drill' | 'extension-cord' | 'garden-hose' | 'spray-can' | 'caulk-tube' | 'screwdrivers' | 'pliers' | 'bits-hex-keys' | 'tape-measure' | 'paper-towel' | 'filament-spool' | 'tape-roll' | 'helmet' | 'cap' | 'safety-glasses' | 'power-strip' | 'cables' | 'phone-tablet' | 'headphones' | 'labels' | 'bins-tubs' | 'small-parts' | 'jacket-bag' | 'shelf-board' | 'fishing-rod' | 'ball' | 'trash-bags' | 'gloves-rags' | 'steel-hooks' | 'spray-bottle' | 'paint-can' | 'wire-basket', received 'garden-gnome'",
  "fix": {
    "say": "Set holds to a list of at most 8 different ids, each one of \"broom-mop\", \"rake-shovel\", \"drill-battery\", \"drill\", \"extension-cord\", \"garden-hose\", \"spray-can\", \"caulk-tube\", \"screwdrivers\", \"pliers\", \"bits-hex-keys\", \"tape-measure\", \"paper-towel\", \"filament-spool\", \"tape-roll\", \"helmet\", \"cap\", \"safety-glasses\", \"power-strip\", \"cables\", \"phone-tablet\", \"headphones\", \"labels\", \"bins-tubs\", \"small-parts\", \"jacket-bag\", \"shelf-board\", \"fishing-rod\", \"ball\", \"trash-bags\", \"gloves-rags\", \"steel-hooks\", \"spray-bottle\", \"paint-can\" or \"wire-basket\". Use [] if none fit."
  }
}
```

**A wall a brick can't take (R4).** On Gladiator GearWall, whose slot opening is 17 mm, a design using the `cam-lock-mount` and `dovetail-slide` bricks gets one issue for the design, at the first of them:

```json
{
  "rule": "R4",
  "code": "wall-compatibility",
  "level": "error",
  "path": "uses.mount",
  "wall": "gladiator",
  "message": "Won't fit this wall: its slot opening is 17 mm, and the cam-lock mount and the dovetail slide need a slot opening from 8.5 to 11 mm.",
  "fix": {
    "say": "On Gladiator GearWall the slot is 17 mm (slot_opening, patent) and the cam-lock mount and the dovetail slide take 8.5 to 11 mm: 6 mm too wide (too loose). No setting changes the walls a brick takes. Registered mount bricks that take this wall: clip-label, label-clip, press-in-label, press-in-holder."
  }
}
```

On a wall that isn't the draft's own, there is nothing to fix: the design simply doesn't claim that wall, and its fit row says won't fit with this reason. On the draft's own wall, see the next section.

The next examples come from real builds on VEVOR PVC slatwall, made by the same checks the studio runs.

**A shape behind the slat face (R2).** A 10 mm box, "stub", added to the `top-wedge` example's hook part, from 5 mm behind the face to 5 mm in front, gives:

```json
{
  "rule": "R2",
  "code": "reaches-into-slot",
  "level": "error",
  "path": "parts.hook",
  "wall": "vevor",
  "message": "Part \"hook\" reaches 5.0 mm into a slot on VEVOR PVC slatwall without a mount brick: the seat slot (y 0.0 to 9.6 mm), at y 4.4 to 10.0 mm, z 0.0 to 10.0 mm. It also cuts about 20.8 mm³ into the slat as drawn. Only mount bricks (cam-lock-mount, hook-arm, cradle, shelf-bracket, multi-slot-lock, clip-label, label-clip, press-in-label, press-in-holder, calibration, cam-tuning, top-wedge) may engage the wall.",
  "fix": {
    "say": "Shape \"stub\" reaches 5.0 mm behind the slat face. Move it out of the wall: add at least 5.0 to x in its move (the patch adds 5.1).",
    "patch": [
      {
        "op": "replace",
        "path": "/shapes/stub/move",
        "value": [0.1, 0, 0]
      }
    ]
  }
}
```

**A corner that fails (R4).** The same example with its `width` setting's `min` lowered to 15 mm builds at its defaults, but the full check also builds 15 mm:

```json
{
  "rule": "R4",
  "code": "wedge-too-wide",
  "level": "error",
  "path": "uses.wedge",
  "wall": "vevor",
  "corner": {
    "setting": "width",
    "value": 15
  },
  "message": "Hook too narrow for the top wedge: the base plate must be at least as wide as the button (width ≥ 17.4 mm). (on VEVOR PVC slatwall with width = 15)",
  "fix": {
    "say": "Raise \"width\" to at least 17.5 mm: the patch sets its min to 17.5.",
    "patch": [
      {
        "op": "replace",
        "path": "/settings/width/min",
        "value": 17.5
      }
    ]
  }
}
```

The fix is the setting's range: raise `min` to what the brick takes. The `corner` names the setting and the value that failed.

**A shape in the key's swing (R8).** A 3 × 10 × 10 mm box, "block", added to the same example's hook part in front of the spine, below the hook seat, gives:

```json
{
  "rule": "R8",
  "code": "collision",
  "level": "error",
  "path": "uses.mount",
  "wall": "vevor",
  "message": "The key (\"mount.key\", brick cam-lock-mount@1) hits shape \"block\" of part \"hook\" as it turns on VEVOR PVC slatwall: from 37.9° to 91.5° of its -5.0° to 91.5° turn, worst at 86.1° with 105.37 mm³ of overlap, up to 1.70 mm deep.",
  "fix": {
    "say": "Move shape \"block\" out of the key's swing: it turns about the axis through (0, -70.59, 6.1) along (-1, 0, 0), sweeping a cylinder of radius 18.00 mm around it from (12.8, -70.59, 6.1) to (-9.5, -70.59, 6.1) (design coordinates). Keep shape \"block\" at least 0.30 mm (fit.print_in_place) clear of that, so at least 2.00 mm more than now, or leave it out of parts.hook. In a formula the axis is at y = mount.YK and z = mount.ZC, which follow every wall; the numbers above are for VEVOR PVC slatwall."
  }
}
```

The numbers in the fix are for one wall: on VEVOR PVC slatwall the readouts "mount.YK" and "mount.ZC" are -70.59 and 6.1, and on another wall they move with the slot. A shape that must clear the swing on every wall follows the readouts the fix names, never those numbers: see R8 below.

**The load (R9, warnings only).** Every load finding is information, never a failure: see [presets-and-checks.md](presets-and-checks.md) for the codes and [catalog.md](catalog.md) for how the load is calculated.

## When the person's wall refuses a brick you chose

The quick check runs on the person's own wall (the wall in their setup, kept for the draft's life). A brick that refuses that wall (a mount, or a joint like `dovetail-slide`) fails the quick check there, and no change to its settings fixes it: use bricks that take the person's wall, starting with a mount (`list_bricks` marks the mounts `mount: true`). If no mount takes it for the part they want, say so plainly.

- On Gladiator GearWall, `calibration`, `cam-lock-mount`, `cam-tuning`, `cradle`, `dovetail-slide`, `drop-dovetail`, `hook-arm`, `multi-slot-lock`, `shelf-bracket` and `top-wedge` refuse it; of the mounts, only `clip-label`, `label-clip`, `press-in-label` and `press-in-holder` take it.

## Problems only a build finds

Some rules need the part built. Their problems come from the quick and full checks with the part, wall and state they happen on.

- R2 Mounting. Everything behind the slat face belongs to the wall. On every claimed wall, each part less the mount bricks' own parts (where their brick put them) must stay in front of the face; touching it is fine up to 0.05 mm. A part that reaches into a slot, or behind the face, fails with how deep it goes and where. The message lists the mount bricks this studio has: they are the only way to engage the wall, and `list_bricks` marks them `mount: true`. To fix it, move or shorten your shape so it stops at the face (x = 0 for hooks, z = 0 for labels). A mount brick's part that you move inside a shape counts as your own geometry.
- R4 Every wall. The design must build with no errors on every claimed wall, at the defaults and at the min and max of every simple setting, one at a time. A failing corner names the setting and the value. Fix it by narrowing that setting's range or raising its default, as the fix says.
- R5 Solid parts. A piece thinner than 1.2 mm, added by your shapes or left by your cutters, fails. Thicken it or remove it.
- R8 Motion. Moving brick parts (keys and their levers) are turned through their whole travel against every other part. Keep your shapes out of their way. On the cam-lock mounts the key turns in the slot below the hook seat (`arm_root`), and its lever sweeps a disc round the key's axis in front of the spine: a shape that hangs below the seat, or a range that lets one grow down, meets it, often only at a corner the full check builds (the example above). Plan for it before the first draft:
  - Keep shapes clear by where they start and how far their ranges let them grow; narrowing the range a corner names is often the whole fix.
  - A shape that must clear the swing on every wall follows the brick's readouts, never the numbers in a fix: the axis is at y = "mount.YK", z = "mount.ZC" (the fix names them; a formula reads them as "instance.readout"). Subtract a cylinder placed there, with its radius from the fix plus `fit.print_in_place`.
  - No readout gives the swing's radius. Take it from the fix, add a margin of at least `fit.print_in_place`, and tell the person it is a number the brick fixes today. If a design needs more than that, it needs a new core piece (see [core.md](core.md)).
- The load (R9, warnings only). The studio calculates what the part holds for stated print assumptions, as information; see [catalog.md](catalog.md).

A fit row that says won't fit on another wall is not a problem to fix in the design: the design doesn't claim that wall. The reason says which brick refuses it and why. On the draft's own wall, see the section above.
