Quickstart#
A minimal valid .kdl skeleton
Get a first draft through design_compile (or ww check) with zero diagnostics before you add
anything real. Then use features/INDEX.md for the syntax of whatever
comes next, nodes.md to find the file for a node, and tasks.md
for a "how do I..." jump-in. The language contract is docs/KDL_AUTHORING.md.
Four rules cause most first-draft diagnostics:
- Every dimension is a float (
19.0, never19), andunitsmust bemm. - Z is negative going down from a part's top face (
z=0.0is the top). - A quoted string is a CEL expression; a bare word is a name or enum
(
x="width / 2.0",normal=pos_x). - A joint is
join joint=<Name> male=Part@Mark female=Part@Mark, unquotedPart@Mark; the wordconnectis dead.
// A minimal one-part project: a single board, no joinery.
project name="Test Shelf" units=mm schema_version=1 {
params {
width 600.0 min=300.0 max=1200.0 step=10.0 label="Width" category="Envelope"
depth 250.0 min=150.0 max=400.0 step=10.0 label="Depth" category="Envelope"
thick 19.0 min=12.0 max=32.0 step=1.0 label="Thickness" category="Stock"
}
materials {
material WhiteOak type=hardwood
}
assembly Root {
part Board material=WhiteOak role=shelf {
stock WhiteOak width depth thick
by top_sw
pos x=0.0 y=0.0 z=0.0
}
}
}
Adding a second part and a joint#
A real design uses component (a reusable template with marks to join on) and joinery +
join (the two-body contract). Here an apron's tenon enters a leg. The apron's mark sits on
the shoulder, the end of its stock line, facing out (neg_x); the leg's mark sits on the
face the tenon enters, facing the apron (pos_x). Their positions must line up when placed.
project name="Test Frame" units=mm schema_version=1 {
params {
leg_dim 45.0 min=30.0 max=64.0 step=1.0 label="Leg Section" category="Stock"
leg_len 700.0 min=400.0 max=900.0 step=10.0 label="Leg Length" category="Stock"
apron_len 400.0 min=200.0 max=1200.0 step=10.0 label="Apron Length" category="Stock"
apron_h 100.0 min=60.0 max=140.0 step=5.0 label="Apron Height" category="Stock"
apron_th 20.0 min=16.0 max=25.0 step=1.0 label="Apron Thickness" category="Stock"
glue_clearance 0.2
}
materials {
material Ash type=hardwood
}
component Leg role=leg {
stock Ash leg_dim leg_dim leg_len
mark MortiseFace_X x=leg_dim y="leg_dim / 2.0" z=-55.0 normal=pos_x
}
component Apron role=apron {
stock Ash apron_len apron_th apron_h
mark Shoulder x=0.0 y="apron_th / 2.0" z="0.0 - apron_h / 2.0" normal=neg_x
}
joinery mortise_and_tenon name=ApronJoint {
tenon_thickness 8.0
tenon_length 25.0
clearance glue_clearance
centering axis=z
}
assembly Root {
part Leg_FL instance=Leg { by top_sw; pos x=0.0 y=0.0 z=0.0 }
part Apron_Front instance=Apron {
by top_sw
pos x=leg_dim y="(leg_dim - apron_th) / 2.0" z=-5.0
}
join joint=ApronJoint male=Apron_Front@Shoulder female=Leg_FL@MortiseFace_X
}
}
Validate#
ww check your_file.kdl # clashes, joint contacts, and the joinery verdicts
cargo run --manifest-path tools/kdl-check/Cargo.toml -- your_file.kdl # lint only
kdl-check is a linter: it cannot see geometry, so a green lint is not a joint that builds.
Run ww check (or design_compile over MCP) and read its diagnostics. Joinery kinds that
refuse an impossible configuration say why in the diagnostic; the family file in
features/ names the rules.