← Skills

Wood Joints

Native Read-only

Joinery geometry rules: mortise/tenon, dado, rabbet, dovetail, box joint, and biscuit joint construction.

/skills/wood_joints.md

Estimated tokens
1120
Characters
4479
Source
Native

Markdown

# Wood Joints Skill

A joint component is a **Board with a modifier**. Build the board first, then chain the joint cut. Each component agent generates one body — never two.

## Mortise (the receiving board — has a rectangular pocket)

```python
from elixifree.domains.woodworking import Board

LENGTH = 700   # mm — board length
WIDTH = 40     # mm
THICKNESS = 40 # mm
# Mortise centered on end face
MORTISE_W = 12
MORTISE_H = 40
MORTISE_D = 32

result = (
    Board(length=LENGTH, width=WIDTH, thickness=THICKNESS, material="oak", grain="long")
    .mortise("end", WIDTH/2 - MORTISE_W/2, THICKNESS/2 - MORTISE_H/2, MORTISE_W, MORTISE_H, MORTISE_D)
    .build()
)
result.add_to_doc("Body")
```

## Tenon (the protruding board — has a peg that fits the mortise)

```python
from elixifree.domains.woodworking import Board

LENGTH = 400   # mm
WIDTH = 40     # mm
THICKNESS = 40 # mm
TENON_W = 12
TENON_H = 40
TENON_L = 32

result = (
    Board(length=LENGTH, width=WIDTH, thickness=THICKNESS, material="oak", grain="long")
    .tenon("end", WIDTH/2 - TENON_W/2, THICKNESS/2 - TENON_H/2, TENON_W, TENON_H, TENON_L)
    .build()
)
result.add_to_doc("Body")
```

## Apron with tenon at each end (joins two legs — standard apron pattern)

```python
from elixifree.domains.woodworking import Board

LENGTH = 500   # mm
WIDTH = 80     # mm
THICKNESS = 22 # mm
TENON_W = 12
TENON_H = 40
TENON_L = 32

result = (
    Board(length=LENGTH, width=WIDTH, thickness=THICKNESS, material="oak", grain="long")
    .tenon("end", WIDTH/2 - TENON_W/2, THICKNESS/2 - TENON_H/2, TENON_W, TENON_H, TENON_L)
    .tenon("start", WIDTH/2 - TENON_W/2, THICKNESS/2 - TENON_H/2, TENON_W, TENON_H, TENON_L)
    .build()
)
result.add_to_doc("Body")
```

## Dado (shelf receiver — cross-grain groove)

```python
from elixifree.domains.woodworking import Board

LENGTH = 800
WIDTH = 250
THICKNESS = 18

result = (
    Board(length=LENGTH, width=WIDTH, thickness=THICKNESS, material="birch_ply", grain="face")
    .dado("top", LENGTH/2 - 9, 18, 6)  # x, width, depth
    .build()
)
result.add_to_doc("Body")
```

## Rabbet (edge step — backs, drawer bottoms)

```python
from elixifree.domains.woodworking import Board

LENGTH = 600
WIDTH = 200
THICKNESS = 18

result = (
    Board(length=LENGTH, width=WIDTH, thickness=THICKNESS, material="birch_ply", grain="face")
    .rabbet("back", 12, 9)  # width, depth
    .build()
)
result.add_to_doc("Body")
```

## Mortise + Tenon modifier signature reference

```
.mortise(face, x, z, width, height, depth)
  face   — "end" (standard) or "top"/"side" for through-mortise
  x, z   — corner position of the pocket within that face
  width  — mortise width (across grain)
  height — mortise height (along grain)
  depth  — how far the pocket goes into the board

.tenon(face, x, z, width, height, length)
  face   — "end" (standard)
  x, z   — corner position of the tenon base
  width, height — tenon cross-section (match mortise width × height)
  length — tenon protrusion (match mortise depth)
```

## Catalog proportions (use these unless the design explicitly overrides)

| Material band | Thickness | Mortise W | Mortise H | Depth |
|---------------|-----------|-----------|-----------|-------|
| hardwood thin (<25mm) | any | 8 | 25 | 20 |
| hardwood medium (25–50mm) | any | 12 | 40 | 32 |
| hardwood thick (>50mm) | any | 16 | 50 | 40 |
| softwood medium | any | 12 | 38 | 28 |

## Joint type selection rules

| Load case | Joint |
|-----------|-------|
| Racking (chair legs, table aprons) | `mortise_tenon` — non-negotiable for lateral loads |
| Shelf support (bookcase dadoes, bed slats) | `dado` preferred; `pocket_screw` for softwood/sheet |
| Face frames, backs | `pocket_screw` or `biscuit` |
| Alignment only (edge glue-up) | `dowel` or `biscuit` |
| Knock-down / quick assembly | `butt_screw` with counterbore |

## When you have two boards and need both cut at once (assembly scripts only)

`Joint` is a convenience that queues the same modifiers on both boards in one call. Only use it when generating an assembly-level script that builds multiple bodies.

```python
from elixifree.domains.woodworking import Board, Joint

leg   = Board(700, 40, 40, "oak", grain="long")
apron = Board(400, 40, 40, "oak", grain="long")
leg, apron = Joint(leg, apron, "mortise_tenon").apply()
result_leg   = leg.build()
result_apron = apron.build()
# add each to doc separately
```

Do NOT use `Joint` in a component agent script — the agent generates one body at a time.
v0.0.985