← Skills

SIP Foundation Construction

Native Read-only

Concrete slab and strip foundation geometry, DPC placement, slab overhang rule, and the NON-NEGOTIABLE foundation agent naming rule.

/skills/sip_foundations.md

Estimated tokens
3020
Characters
12077
Source
Native

Markdown

# SIP Foundation Construction Skill

## FIRST: which foundation are you building?

Read the goal string and pick the row. This decides your imports — get it wrong and
the script is rejected before it runs.

| Goal says | Build with | First import line |
|-----------|------------|-------------------|
| "strip", "strip perimeter", "perimeter foundation", "trench fill" | `Foundation` builder, `type="strip_foundation"` — **section 2** | `from elixifree.domains.sip import Foundation` |
| "screw pile", "pile", "helical" | `Foundation` builder — **section 3** | `from elixifree.domains.sip import Foundation` |
| "slab", "raft", "slab-on-grade" | raw `Part` — **section 1** (no builder exists) | `import FreeCAD, Part` **plus a `# ElixiFree gap:` line — see below** |

**A strip foundation written with raw `Part.makeBox` is rejected.** The check is
mechanical: a script that neither imports `elixifree` nor carries an
`# ElixiFree gap: <reason>` comment fails and is regenerated. Most SIP foundations in
this system are strip foundations, so `from elixifree.domains.sip import Foundation`
is almost always your first line.

Anchor-bolt detail in the goal ("M12 anchor bolts at 600mm centres") describes
fixings placed deterministically downstream. It is not a reason to hand-write
geometry, and it does not change which builder you use.

## Foundations

### 1. Concrete Slab — raw `Part`, gap comment REQUIRED

There is no ElixiFree builder for slab-on-grade, so this is the one foundation you
write with raw `Part` geometry. Because the script will not import `elixifree`, it
MUST declare the gap or it is treated as a generation error:

```python
# ElixiFree gap: no slab-on-grade builder in elixifree.domains.sip — raw Part geometry
import FreeCAD, Part
from FreeCAD import Vector
```

The slab MUST extend beyond the wall footprint on all sides (`SLAB_OVERHANG = 200mm` minimum).

```
Section view:
 ┌──────────────────────────────────────────────┐
 │  SIP wall panel (foam min 150mm above ground)│
 ├──────────────────────────────────────────────┤ ← PT bottom plate (45×90mm)
 ├──────────────────────────────────────────────┤ ← DPC membrane (3mm)
 ├──────────────────────────────────────────────┤ ← slab top (Z=0)
 │           concrete slab 125mm                │
 ├─────┬────────────────────────────────┬───────┤
 │edge │    insulation (optional)       │ edge  │
 │beam │                                │ beam  │
 │300× │                                │ 300×  │
 │600  │                                │ 600   │
 └─────┴────────────────────────────────┴───────┘
    ← SLAB_OVERHANG (200mm min) →
    ← slab extends past wall face on all sides
```

```python
import FreeCAD, Part
from FreeCAD import Vector

# === SLAB PARAMETERS ===
BUILDING_WIDTH = 3000    # outer wall face to outer wall face
BUILDING_DEPTH = 2000
SLAB_OVERHANG = 200      # slab extends beyond wall footprint — minimum 200mm
SLAB_W = BUILDING_WIDTH + 2 * SLAB_OVERHANG
SLAB_D = BUILDING_DEPTH + 2 * SLAB_OVERHANG
SLAB_T = 125             # slab field thickness
EDGE_BEAM_W = 300        # thickened perimeter beam width
EDGE_BEAM_D = 600        # total depth including slab thickness
DPC_T = 3                # damp proof course
CORE_THICKNESS = 150     # foam core of the SIP panel (SIP-150)
FACE_THICKNESS = 11      # OSB skin each side
SILL_PLATE_W = CORE_THICKNESS   # plate slots into foam groove — NOT full SIP thickness
SILL_PLATE_H = 90

# Z=0 = slab top (all wall and floor references start here)
slab_x0 = -SLAB_OVERHANG
slab_y0 = -SLAB_OVERHANG

doc = FreeCAD.ActiveDocument or FreeCAD.newDocument("Foundation")

# Slab field
slab = Part.makeBox(SLAB_W, SLAB_D, SLAB_T, Vector(slab_x0, slab_y0, -SLAB_T))

# Edge beams (perimeter thickening)
eb_n = Part.makeBox(SLAB_W, EDGE_BEAM_W, EDGE_BEAM_D,
                    Vector(slab_x0, slab_y0 + SLAB_D - EDGE_BEAM_W, -EDGE_BEAM_D))
eb_s = Part.makeBox(SLAB_W, EDGE_BEAM_W, EDGE_BEAM_D,
                    Vector(slab_x0, slab_y0, -EDGE_BEAM_D))
eb_e = Part.makeBox(EDGE_BEAM_W, SLAB_D, EDGE_BEAM_D,
                    Vector(slab_x0 + SLAB_W - EDGE_BEAM_W, slab_y0, -EDGE_BEAM_D))
eb_w = Part.makeBox(EDGE_BEAM_W, SLAB_D, EDGE_BEAM_D,
                    Vector(slab_x0, slab_y0, -EDGE_BEAM_D))

foundation = slab.fuse(eb_n).fuse(eb_s).fuse(eb_e).fuse(eb_w)

# Anchor bolts M12 at 600mm centres (perimeter, modelled as cylinders)
BOLT_D = 12
BOLT_L = 150
for x_pos in range(300, BUILDING_WIDTH - 300, 600):
    bolt = Part.makeCylinder(BOLT_D / 2, BOLT_L,
                              Vector(x_pos, FACE_THICKNESS + SILL_PLATE_W / 2, -BOLT_L))
    foundation = foundation.fuse(bolt)

# DPC membrane strip and sill plate sit at Z=0 on slab surface.
# Offset by FACE_THICKNESS so they align with the foam channel.
dpc_s = Part.makeBox(BUILDING_WIDTH, SILL_PLATE_W, DPC_T,
                     Vector(0, FACE_THICKNESS, 0))
sp_s  = Part.makeBox(BUILDING_WIDTH, SILL_PLATE_W, SILL_PLATE_H,
                     Vector(0, FACE_THICKNESS, DPC_T))
# Repeat for N, E, W walls

feature = doc.addObject("Part::Feature", "Foundation")
feature.Shape = foundation
doc.recompute()
```

**Key rules for slab foundations:**
- Slab top surface = Z=0 (all wall/floor heights reference from this)
- `SLAB_OVERHANG` ≥ 200mm — if SLAB_W or SLAB_D equals BUILDING_WIDTH/DEPTH, the slab is too small
- Foam core bottom = DPC_T + SILL_PLATE_H = must be ≥ 150mm above finished ground
- Anchor bolts: M12, 150mm embedment, 600mm centres, 150mm from corners

---

### 2. Strip Foundation — use `Foundation` builder

**Use the `Foundation` builder with `type="strip_foundation"`.** Do not write raw `Part.makeBox` fuse code for the strip ring — the builder handles segment geometry internally and is tested.

For an L-shaped building, pass the exact ordered OUTER concrete footprint to
`footprint=`. Do not reduce it to a bounding rectangle and do not inset the polygon
again in the component script. The builder preserves the re-entrant notch, creates
the hollow perimeter strip, and keeps its top face at local Z=0.

```python
from elixifree.domains.sip import Foundation

OUTER_CONCRETE_FOOTPRINT = [
    [0, 0], [18284, 0], [18284, 5284],
    [12142, 5284], [12142, 7284], [0, 7284],
]

result = Foundation(
    footprint=OUTER_CONCRETE_FOOTPRINT,
    depth=400,
    type="strip_foundation",
    strip_w=450,
).build()
feature = result.add_to_doc("Body")
```

The footprint vertices are the component's local outer boundary. Its bounding-box
anchors therefore use the min/max of those vertices. Apply world placement only once
through the assembly placement contract.

```python
from elixifree.domains.sip import Foundation
from FreeCAD import Placement, Rotation, Vector

# === PARAMETERS — read every value from the goal string ===
BUILDING_WIDTH = 6000
BUILDING_DEPTH = 4000
STRIP_W = 450           # strip width in plan
STRIP_D = 300           # strip depth (always positive)

result = Foundation(
    length=BUILDING_WIDTH,
    width=BUILDING_DEPTH,
    depth=STRIP_D,
    type="strip_foundation",
    strip_w=STRIP_W,
).build()
feature = result.add_to_doc("Body")
# MANDATORY: strip sits directly under the wall outer faces, no offset
feature.Placement = Placement(Vector(0, 0, -STRIP_D), Rotation())
```

**`length` and `width` are the OUTER concrete footprint** — they equal
`BUILDING_WIDTH` / `BUILDING_DEPTH` directly. `BUILDING_WIDTH`/`BUILDING_DEPTH`
are already the outer-to-outer wall envelope (eave walls span the full
`BUILDING_WIDTH`, see `sip_decomposition.md`), so do **not** add
`2 × PANEL_THICKNESS` — that produces a strip that projects a full panel
thickness beyond the wall's own outer face on every side, visibly outside
the building shell instead of directly under the wall panels (ISSUE-013).
External finishing/cladding is what should overhang the foundation edge, not
the foundation overhanging the walls. The hollow centre is `length −
2×strip_w` by `width − 2×strip_w`.

**Z reference:** Z=0 is the top face of the strip (sole plate bearing face). Strip extends downward to Z=−STRIP_D. The Placement offset puts Z=0 at the strip's top face after positioning.

**Key rules:**
- ONE agent (`foundation`), ONE model — never split into four side agents
- Strip top face at Z=0 is the bearing face for sole plates — no foundation wall above it
- `strip_w` defaults to 450mm if omitted

---

### 3. Screw Pile Foundation

No ElixiFree builder — write raw `Part` geometry. Steel screw piles driven to bearing depth, carrying LVL bearer beams.

```python
import FreeCAD, Part
from FreeCAD import Vector

# === SCREW PILE PARAMETERS ===
PILE_DIAMETER = 114
PILE_SPACING_X = 2400
PILE_SPACING_Y = 2400
PILE_EXPOSED_H = 600
BEARER_W = 90
BEARER_H = 190

doc = FreeCAD.ActiveDocument or FreeCAD.newDocument("Foundation")

piles_x = (BUILDING_WIDTH // PILE_SPACING_X) + 1
piles_y = (BUILDING_DEPTH // PILE_SPACING_Y) + 1

parts = []
for ix in range(piles_x):
    for iy in range(piles_y):
        x = ix * PILE_SPACING_X
        y = iy * PILE_SPACING_Y
        pile = Part.makeCylinder(PILE_DIAMETER / 2, PILE_EXPOSED_H,
                                 Vector(x, y, -PILE_EXPOSED_H))
        parts.append(pile)

for iy in range(piles_y):
    y = iy * PILE_SPACING_Y
    b1 = Part.makeBox(BUILDING_WIDTH, BEARER_W, BEARER_H, Vector(0, y, 0))
    b2 = Part.makeBox(BUILDING_WIDTH, BEARER_W, BEARER_H,
                      Vector(0, y + BEARER_W + 10, 0))
    parts.extend([b1, b2])

result = parts[0]
for p in parts[1:]:
    result = result.fuse(p)

feature = doc.addObject("Part::Feature", "Foundation")
feature.Shape = result
doc.recompute()
```

---

## Building Assembly — Coordinate System

```
Origin (0, 0, 0) = SW corner of the building footprint at Z = 0 (top of slab / floor level).
X-axis = East (along building width)
Y-axis = North (along building depth)
Z-axis = Up
```

The slab extends `SLAB_OVERHANG` beyond the building footprint on all four sides.

---

## MANDATORY: Foundation Component

**Every SIP building manifest MUST include a `foundation` component.** No exceptions. The foundation is always generated — even when the user hasn't mentioned it. It is the root of the dependency tree.

**NON-NEGOTIABLE — agent id MUST be exactly `foundation`:**

❌ FORBIDDEN agent ids:
- `concrete_slab`, `slab`, `strip_foundation`, `foundation_slab`
- `base_plate` / `base_plate_south` / `base_plate_north`
- `foundation_front` / `foundation_south` / `foundation_strip_north`

✅ CORRECT: one agent, id = `foundation`, priority = 1, no dependencies.

**Rules:**
- Agent id: `foundation` (exactly — see forbidden list above)
- Priority: 1 (no dependencies)
- Foundation type comes from the goal string — read it before choosing imports (see
  the table at the top). Do not assume slab: strip perimeter foundations are just as
  common, and they MUST use the `Foundation` builder, not raw `Part`.
- The foundation is modelled as a single piece — never split into four sides

**Goal string template (slab):**
```
Create slab-on-grade foundation, BUILDINGWIDTHxBUILDINGDEPTHmm slab overhang 200mm all sides, 125mm slab thickness, 600mm edge beam depth, 300mm edge beam width. Single fused solid. Z=0 is slab top. Place at Vector(-SLAB_OVERHANG, -SLAB_OVERHANG, -SLAB_THICKNESS) so the slab extends DOWN from z=0 (slab top at z=0, walls bear on it).
```

The Z offset MUST be `-SLAB_THICKNESS` (negative), not 0. With `Vector(..., 0)` the slab
sits ABOVE the floor (z=0..thickness) and the walls embed into it. Example for a 200mm
slab with 200mm overhang: `Place at Vector(-200, -200, -200)`.

**Goal string template (strip):**
```
Create strip perimeter foundation: BUILDINGWIDTHxBUILDINGDEPTHmm footprint, strip depth 300mm, strip width 450mm. Outer footprint matches the wall envelope exactly — BUILDING_WIDTHxBUILDING_DEPTHmm (do NOT add wall panel thickness). Place at Vector(0, 0, -300).
```

---

## ⚠ Non-Negotiable Foundation Rule

**Foundation slab extends beyond the wall footprint on all sides.**
`SLAB_W = BUILDING_WIDTH + 2 × SLAB_OVERHANG` (minimum 200mm). A slab the same size as the wall footprint leaves no bearing margin.
v0.0.985