# Joinery Contract

*The two-body contract between joinery and join: marks, clearance, and the kinds table*

**Names:** `joinery` `join` `male` `female` `joint` `clearance` `fit` `butt` `name` `grain_check`

## When to use

Every time two parts are joined. A joint is **declared by the author, never inferred** (D8):
you name the joint type once with a `joinery` block, then apply it with `join` between two
marks. The compiler cuts the male feature on one part and the female cavity on the other at
the same time. Which kind to write is the next question: use the kinds table below to find the
family file.

## Syntax

```text
joinery <kind> name=<Name> {
    <param> <value>...            // kind-specific; see the family file
    clearance <expr>              // female side only, grown by clearance / 2 per face
    fit slip|snug|press           // instead of clearance=: resolve the female material's own
    grain_check off reason="..."  // opt out of the grain-contact error/rating; reason required
}
join joint=<Name> male=<Part>@<Mark> female=<Part>@<Mark>
```

- `male=`/`female=` are **`Part@Mark`**, written **unquoted** (ADR-0002). The male part
  receives the protruding feature (tenon, tongue, pin); the female receives the cavity.
- A reference whose part id contains a `repeat` interpolation must be **quoted** because `{`
  is structural in KDL: `male="TopBoard_{j}@EdgeGlue_N"`. A literal id stays bare.
- `join` lives in an `assembly`. The `joinery` block is project-level and reusable by name.
- `repeat` inside a `joinery` body writes a schedule (graduated dovetail `pin` rows).
- `grain_check off reason="..."` opts a joint out of the grain-contact error and the
  banded strength rating (never the movement warning, a separate concern) — see
  `docs/KDL_AUTHORING.md` §8's "Grain contact and the joint-strength rating" and
  `docs/adr/0082-grain-contact-and-joint-strength-rating.md`. `reason=` is required.

### Kinds and the file that documents each

| Kind | Key params | File |
| --- | --- | --- |
| `butt` | none (needs no marks) | here |
| `dowel` `pocket_hole` `biscuit` `domino` `edge_glue` | fastener rows | `joinery-fasteners` |
| `mortise_and_tenon` `round_tenon` | tenon family (pin, wedge, key, through, proud) | `joinery-tenons` |
| `half_lap` `cross_lap` `bridle` `box_joint` `dovetail_through` `dovetail_half_blind` `sliding_dovetail` | laps, bridles, box and dovetail joints | `joinery-laps-and-boxes` |
| `miter` `stile_and_rail` | mitres and frame-and-panel | `joinery-frames` |
| `tongue_and_groove` | declared only; a newer file in `INDEX.md` may build it | see `INDEX.md` |
| `hardware_mount` | `hardware <Name>`; cuts nothing | `materials-and-hardware` |

A kind that is declared but not yet cut is resolved, drawn and reported as "not yet
synthesised" by `ww joins`, `ww check` and the viewer.

## The layout rule every cut joint shares (M5, ADR-0013)

A joint is **laid out from its male mark**, on the stock face the mark sits on and faces.

- The two marks' `normal`s must face each other (opposite in world space) on faces that are in
  contact, and the male mark must lie where the faces overlap. Where the female mark sits
  along its face is not used.
- A standard anchor (`top_sw`) has no facing, so a joint that cuts anything needs `mark`s on
  both parts. Only `butt` needs none.
- **`clearance` goes on the female side only**: its hole, slot or mortise is the male one grown
  by `clearance / 2` on every face.
- **Clearance resolution (issue #55).** `clearance=` always wins when given. With neither
  `clearance=` nor `fit=`, or `fit=` alone: the female material's `tolerance_slip=`/
  `tolerance_snug=`/`tolerance_press=` (`materials-and-hardware`), for the named class or
  `snug` by default; else the project's `glue_clearance` param; else `0.2`. `clearance=` and
  `fit=` together is an error. See `docs/KDL_AUTHORING.md`'s "Clearance resolution" for the
  full chain and the draft per-material starting values.
- **Refused** (nothing cut; `ww check` fails): marks that do not face each other, faces not in
  contact (an inset door's reveal), faces that do not overlap or are too short for the
  fastener, a fastener wider than the shared overlap or one that does not fit its stock,
  fasteners closer than their own size, a hole deeper than the board, holes from opposite faces
  that meet, and a parameter missing or out of range.
- **Warned:** less wood left behind a blind cut than `max(3 mm, 15 %)` of the board, and a
  fractional `count`.
- A mark on no real stock face fails at `join` time. Oblique shoulders and `tilt=`/`heading=`
  marks are handled by the tenon family; see `components-and-stock`.

## Example

```kdl
project name="Joined Boards" units=mm schema_version=1 {
    params { glue_clearance 0.2 }
    materials { material Oak type=hardwood }
    component Leg role=leg {
        stock Oak 45.0 45.0 700.0
        mark Face x=45.0 y=22.5 z=-100.0 normal=pos_x
    }
    component Rail role=rail {
        stock Oak 400.0 20.0 80.0
        mark End x=0.0 y=10.0 z=-40.0 normal=neg_x
    }
    joinery mortise_and_tenon name=RailJoint {
        tenon_thickness 8.0
        tenon_length 25.0
        clearance glue_clearance
    }
    assembly Frame {
        part Leg_A instance=Leg { by top_sw; pos x=0.0 y=0.0 z=0.0 }
        part Rail_A instance=Rail { by top_sw; pos x=45.0 y=12.5 z=-60.0 }
        join joint=RailJoint male=Rail_A@End female=Leg_A@Face
    }
}
```

## Pitfalls and diagnostics

- Write `male=Rail_A@End`, never `Rail_A:End` or a quoted string (unless interpolated).
- `join` is a **mutual contract**, not an action on one body; do not order joins or features
  expecting a sequence. `connect` is the removed old spelling of `join`.
- `ww joins` lists each join with its gap, whether the marks meet and the normals' dot
  product (-1.0 is facing squarely); `ww check` adds the clash and joinery verdicts.
- A parameter on a `joinery` block may be a literal or a CEL expression over the project's
  `params`; enums (`slope "1:8"`, `face "'neg_y'"`) have per-kind quoting; see the family file.
- Choose the joint a woodworker would actually use. The validator rejects what cannot be made;
  it cannot tell a poor choice (end-grain pocket screws into hardwood, a mortise into 6 mm
  ply) from a good one.
- Marks that touch but are not `join`ed are not a joint; a `join` is what cuts.

## See also

- `docs/KDL_AUTHORING.md` §8; ADR-0013 (joint framework), ADR-0014 (joint authoring edits),
  ADR-0002 (`Part@Mark`).
- `components-and-stock.md` (marks), `parts-and-assemblies.md` (where `join` sits).
- `joinery-fasteners.md`, `joinery-tenons.md`, `joinery-laps-and-boxes.md`,
  `joinery-frames.md`, `materials-and-hardware.md`.

## Names

Every name this page declares, how the example library uses it, and the shortest real use.

### `joinery`

project-level node; node in 109 of 116 examples (440 uses).

Shortest real use, from `neo_shaker_cherry_sideboard.kdl`:

```kdl
joinery butt name=BackJoint { }
```

### `join`

node in 109 of 116 examples (1962 uses).

Shortest real use, from `desk.kdl`:

```kdl
join joint=PullMount male=Face@PullPoint
```

### `male`

word-valued property; property in 109 of 116 examples (1962 uses).

Shortest real use, from `desk.kdl`:

```kdl
join joint=PullMount male=Face@PullPoint
```

### `female`

word-valued property; property in 108 of 116 examples (1918 uses).

Shortest real use, from `wedged_post_and_rung_stool.kdl`:

```kdl
join joint=LegJ male=Leg_FL@Top female=Seat@LegFL
```

### `joint`

word-valued property; property in 109 of 116 examples (1962 uses).

Shortest real use, from `desk.kdl`:

```kdl
join joint=PullMount male=Face@PullPoint
```

### `clearance`

node in 102 of 116 examples (333 uses).

Shortest real use, from `appalachian_ladder_back_side_chair.kdl`:

```kdl
clearance 0.4
```

### `fit`

word-valued property; not used by any example yet.

### `butt`

joinery kind; value in 20 of 116 examples (21 uses).

Shortest real use, from `neo_shaker_cherry_sideboard.kdl`:

```kdl
joinery butt name=BackJoint { }
```

### `name`

word-valued property; property in 116 of 116 examples (556 uses).

Shortest real use, from `neo_shaker_cherry_sideboard.kdl`:

```kdl
joinery butt name=BackJoint { }
```

### `grain_check`

word-valued property; not used by any example yet.

## Related terms

- [Butt joint](/almanac/techniques#butt-joint)
