Use this skill to validate and compare kitchen/architectural floor plan drawings generated by the 711 N60th FreeCAD BIM pipeline. Triggers when the user asks to: review a floor plan, validate code compliance (clearances, work triangle, GFCI), compare before/after layout changes, check if a drawing matches design intent, or generate a permit-ready compliance summary. Works on SVG, DXF, and PDF files produced by kitchen_permit_docs.py. Do NOT use for general FreeCAD modeling help — use freecad-...
Scanned 9/20/2026
Install to Claude Code
npx -y skills add tstapler/dotfiles --skill freecad-review --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Freecad Review?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/tstapler-freecad-review)More formats (shields.io, HTML) on the badges page.
---
name: freecad-review
description: "Use this skill to validate and compare kitchen/architectural floor plan drawings generated by the 711 N60th FreeCAD BIM pipeline. Triggers when the user asks to: review a floor plan, validate code compliance (clearances, work triangle, GFCI), compare before/after layout changes, check if a drawing matches design intent, or generate a permit-ready compliance summary. Works on SVG, DXF, and PDF files produced by kitchen_permit_docs.py. Do NOT use for general FreeCAD modeling help — use freecad-bim-expert for that."
---
# FreeCAD Drawing Review & Validation
## What This Skill Does
Validates kitchen floor plan drawings from the 711 N60th FreeCAD BIM pipeline against:
1. **IRC code compliance** — clearances, work triangle, GFCI placement, ventilation
2. **Design intent** — appliance positions, island dimensions, traffic flow zones
3. **Layout change detection** — "what moved between revision A and revision B?"
4. **Permit documentation quality** — are drawings complete enough for SDCI submittal?
Produces machine-readable JSON results plus a human-readable compliance summary.
---
## Critical: How Claude Processes Drawing Files
**SVG uploads → XML text extraction only.** Claude reads `<text>` elements but CANNOT see the spatial layout from an SVG file. Do not send SVG expecting visual floor plan analysis.
**PNG → visual analysis.** Claude sees layout and positions but may misread small dimension text.
**Always use both:** rasterize the SVG to PNG for spatial review, and extract SVG `<text>` labels programmatically to inject into the prompt.
---
## 5-Layer Validation Pipeline
### Layer 1: Programmatic Compliance (authoritative — no Claude vision)
Parse SVG/DXF geometry and run Python assertions. This is the ground truth for permit documentation.
```bash
# If DXF is available:
~/.claude/skills/freecad-review/scripts/check_compliance.py \
--dxf "/path/to/kitchen_floor_plan.dxf" \
--output "/tmp/compliance_layer1.json"
# If SVG only:
~/.claude/skills/freecad-review/scripts/check_compliance.py \
--svg "/path/to/kitchen_floor_plan_annotated.svg" \
--output "/tmp/compliance_layer1.json"
```
Output format:
```json
{
"checks": [
{"rule": "island_to_range_clearance", "required_in": 42, "measured_in": 48.5, "result": "PASS"},
{"rule": "work_triangle_perimeter", "required_max_ft": 26, "measured_ft": 19.2, "result": "PASS"},
{"rule": "gfci_island", "result": "CANNOT_VERIFY", "note": "electrical layer not present in DXF"}
],
"overall": "PASS_WITH_WARNINGS",
"warnings": ["GFCI placement requires electrical layer review"]
}
```
### Layer 2: SVG Text Extraction
Before any Claude API call, extract all dimension labels from the SVG source:
```python
import xml.etree.ElementTree as ET
def extract_svg_labels(svg_path):
ns = '{http://www.w3.org/2000/svg}'
tree = ET.parse(svg_path)
return [el.text.strip() for el in tree.iter(f'{ns}text')
if el.text and el.text.strip()]
```
Inject into every Claude prompt as:
```
LABELED DIMENSIONS IN DRAWING:
[list from extract_svg_labels()]
IMPORTANT: Do not estimate any dimension not present in this list.
If a dimension is not explicitly labeled, output "CANNOT_VERIFY".
```
### Layer 3: Claude Dual-Pass Visual Review
Rasterize the drawing first:
```bash
# Using pdf-proof's pre-installed cairosvg:
~/.claude/skills/pdf-proof/.venv/bin/python3 -c "
import cairosvg
cairosvg.svg2png(url='/path/to/drawing.svg', write_to='/tmp/review_2000px.png', output_width=2000)
"
```
**Pass 1 — Element Inventory** (send PNG + extracted labels):
```
System prompt:
You are reviewing a kitchen floor plan. All labeled dimensions are provided in the
text block below. Do not estimate any dimension not in that list — output
CANNOT_VERIFY for anything unlabeled.
User message:
[PNG image attachment]
LABELED DIMENSIONS: {labels_list}
List every named element (appliances, island, counters, walls, openings) and its
approximate zone position. Use these zone names: north-wall, south-counter, east-wall,
west-wall, island, island-north-aisle, island-south-aisle.
Output JSON:
{"elements": [{"name": str, "zone": str, "notes": str, "label_evidence": [str]}]}
```
**Pass 2 — Change Detection** (text only, no image needed):
```
Compare these two kitchen element inventories and identify every element that:
- Moved to a different zone or position
- Was added (present in PROPOSED, not in EXISTING)
- Was removed (present in EXISTING, not in PROPOSED)
EXISTING: {existing_inventory_json}
PROPOSED: {proposed_inventory_json}
For each change, cite specific label evidence. If no evidence is visible, set
confidence to LOW and flag for human review.
Output JSON:
{"changes": [{"element": str, "change_type": "moved"|"added"|"removed",
"from": str, "to": str, "evidence": str, "confidence": "HIGH"|"MEDIUM"|"LOW"}],
"unchanged": [str],
"low_confidence_items": [str]}
```
### Layer 4: Small-Element Checklist
For GFCI outlets, hood, and other elements not visible at full drawing scale,
use a targeted yes/no checklist prompt with cropped sub-images.
Crop relevant zones from the 2000px PNG:
```python
from PIL import Image
img = Image.open('/tmp/review_2000px.png')
# Crop island zone (adjust coordinates to drawing)
island_crop = img.crop((x1, y1, x2, y2))
island_crop.save('/tmp/island_zone.png')
```
Prompt:
```
[island_zone.png attachment]
Confirm presence or absence of each item. Output JSON with present: true/false and
any visible evidence.
Checklist:
- GFCI outlet on island (required within 20" of water source)
- GFCI outlet at sink counter
- Range hood or ceiling-mount ventilation above cooktop
- Exhaust duct direction indicated
```
### Layer 5: Human Escalation
Any item with `"confidence": "LOW"` or `"result": "CANNOT_VERIFY"` is flagged
in the final report with: **REQUIRES HUMAN REVIEW BEFORE PERMIT SUBMISSION**.
---
## IRC Compliance Checklist (Kitchen — IRC 2021)
Use this list for Layer 3 structured checklist prompts:
| Requirement | Rule | Threshold |
|---|---|---|
| Island-to-counter aisle (primary) | IRC R303.3 / NKBA | ≥ 42" (≥ 48" if two cooks) |
| Island-to-range aisle | NKBA G12 | ≥ 48" |
| Work triangle perimeter | NKBA G4 | ≤ 26 ft |
| No leg of work triangle through island | NKBA G4 | Required |
| Refrigerator landing counter | NKBA G8 | ≥ 15" on handle side |
| Range landing counter (each side) | NKBA G9 | ≥ 12" each side |
| Sink landing counter (each side) | NKBA G7 | ≥ 24" on one side, ≥ 18" other |
| GFCI all countertop outlets | NEC 210.8(A)(6) | Within 6 ft of sink |
| GFCI island outlets | NEC 210.8(A)(6) | Required |
| Ventilation CFM | IRC M1503 | ≥ 100 CFM or per hood spec |
| Minimum floor area | IRC R304 | ≥ 70 sq ft |
| Minimum ceiling height | IRC R305 | ≥ 7 ft |
---
## Workflow: Full Review (Single Drawing)
```
1. Locate drawing files in the project directory
2a. Run extract_clearances.py (SVG geometry → programmatic clearances JSON)
2b. Run check_compliance.py --dxf (DXF entity inventory + SVG labels)
3. Rasterize SVG → 2000px PNG using rsvg-convert
4. Run add_grid.py → gridded PNG with 2-ft calibrated cells (A1-G5)
5. Run Layer 3 Pass 1 (element inventory, PNG + labels) → save JSON
6. Run Layer 3 Pass 2 checklist (IRC compliance) → save checklist JSON
7. If GFCI/hood visibility unclear: crop + Layer 4 checklist
8. Assemble final report combining all layers
9. Flag any CANNOT_VERIFY or LOW_CONFIDENCE items
```
## Workflow: Change Detection (Before vs After)
```
1. Identify EXISTING and PROPOSED drawing files
2. Rasterize both → two 2000px PNGs
3. Extract labels from both SVGs
4. Run Layer 3 Pass 1 on each → two inventory JSONs
5. Run Layer 3 Pass 2 (compare inventories) → change diff JSON
6. Optional: Pillow pixel-diff for sub-foot position changes
7. Report: what changed, with evidence and confidence
```
---
## Output: Final Compliance Report
Assemble a human-readable Markdown summary:
```markdown
# Kitchen Compliance Review — {drawing_name}
Date: {date} | Reviewer: Claude {model_version}
## Overall Status: PASS / FAIL / PASS WITH WARNINGS
## Layer 1 — Programmatic Compliance (Authoritative)
| Requirement | Measured | Required | Result |
|---|---|---|---|
| Island-to-range clearance | 48.5" | ≥ 48" | ✅ PASS |
| Work triangle | 19.2 ft | ≤ 26 ft | ✅ PASS |
## Layer 3 — Visual Review
| Element | Zone | Confidence | Notes |
|---|---|---|---|
| Island | center | HIGH | 3'-0" × 4'-6" per labels |
## Changes Detected (if comparison)
| Element | Change | Evidence | Confidence |
|---|---|---|---|
| Island | Moved south 6" | Dimension label shift | MEDIUM |
## Items Requiring Human Review
- [ ] GFCI outlet at island — CANNOT_VERIFY (electrical layer not in drawing)
- [ ] Hood CFM rating — no specification visible
## Notes for SDCI Submittal
[Any permit-specific observations]
```
---
## Dependencies
### System tools (install via Homebrew if missing)
```bash
# SVG rasterization — REQUIRED for Layer 3
brew install librsvg # provides rsvg-convert
# Usage: rsvg-convert -w 2000 drawing.svg -o /tmp/drawing_2000px.png
# PDF rasterization (alternative to rsvg for PDF inputs)
# PyMuPDF (fitz) handles this — see pip section below
# Verify installed:
which rsvg-convert # should print /opt/homebrew/bin/rsvg-convert or /usr/bin/rsvg-convert
```
### Python packages in pdf-proof venv (`~/.claude/skills/pdf-proof/.venv`)
Already installed (verified 2026-05-10):
- `PyMuPDF (fitz)` — PDF rasterization and text extraction
- `Pillow` — image cropping, pixel diff, grid overlay drawing
- `ezdxf` — DXF geometry extraction for Layer 1
If any are missing:
```bash
~/.claude/skills/pdf-proof/.venv/bin/pip install ezdxf Pillow PyMuPDF
```
### Standard library (no install)
- `xml.etree.ElementTree` — SVG text label extraction
### Quick dependency check
```bash
~/.claude/skills/pdf-proof/.venv/bin/python3 -c "
import ezdxf, fitz
from PIL import Image
print('All Python deps OK')
"
which rsvg-convert && echo "rsvg-convert OK"
```
---
## Scripts Reference
All scripts use the pdf-proof venv python: `~/.claude/skills/pdf-proof/.venv/bin/python3`
### `scripts/extract_clearances.py` — SVG geometry → clearances JSON
Reads appliance colored rects from SVG, converts to real-inch positions, computes
all four aisle clearances programmatically. No Claude vision.
```bash
~/.claude/skills/pdf-proof/.venv/bin/python3 \
~/.claude/skills/freecad-review/scripts/extract_clearances.py \
--svg "/path/to/kitchen_floor_plan_annotated.svg" \
--output /tmp/clearances.json
```
**711 N60th results (verified 2026-05-10):**
- West aisle: 46.5" ✅ PASS (≥ 42")
- East aisle: 46.5" ✅ PASS
- North aisle: 55.0" ✅ PASS
- **South aisle: 30.0" ⚠️ FAIL** (bar seating south face to south wall — below 42" min)
### `scripts/check_compliance.py` — DXF layer inventory + SVG label extraction
Parses DXF for entity/layer inventory. Extracts SVG text labels as a list for injection
into Claude prompts.
```bash
~/.claude/skills/pdf-proof/.venv/bin/python3 \
~/.claude/skills/freecad-review/scripts/check_compliance.py \
--dxf "/path/to/drawing.dxf" \
--svg "/path/to/drawing.svg" \
--output /tmp/compliance_layer1.json
```
**711 N60th DXF structure:** 232 LINE entities, layers: `Existing` (192), `Proposed_Remodel` (40). Units: millimeters. Scale: 25.4 mm/inch.
### `scripts/add_grid.py` — Add 2-ft calibrated grid overlay to rasterized PNG
Anchors grid to kitchen interior corners (hardcoded for 711 N60th; override via args).
Draws 2-foot cells A1–G5 (A=west, G=east, 1=north, 5=south). Labels every cell.
```bash
# Step 1: Rasterize SVG
rsvg-convert -w 2000 "/path/to/drawing.svg" -o /tmp/drawing_2000px.png
# Step 2: Add grid
~/.claude/skills/pdf-proof/.venv/bin/python3 \
~/.claude/skills/freecad-review/scripts/add_grid.py \
--svg "/path/to/drawing.svg" \
--png /tmp/drawing_2000px.png \
--out /tmp/drawing_grid.png
```
**Grid cell layout for 711 N60th kitchen (2-ft cells):**
```
A B C D E F G
1 [NW] [Sink] [----] [Fridge] [----] [Cab NE] [----]
2 [----] [----] [----] [----] [----] [----] [----]
3 [----] [----] [Isl/Rng][Isl/Rng][----] [DW] [----]
4 [----] [----] [Bar] [Bar] [----] [----] [----]
5 [----] [----] [----] [G3KH] [----] [Cab SE] [----]
```
Send gridded PNG to Claude with: "Elements are at: Sink=B1, Fridge=D1, Range=C3-D3,
Island=C3-E4, Bar=C4-D4, DW=F3, south aisle=row 5."
### `scripts/rasterize_pdf_page.py` — Extract CD set page as PNG for comparison
Extracts a specific page from the CD set PDF as a high-resolution PNG. Handles scale
mismatch between CD drawings and BIM SVGs. Requires PyMuPDF (already in pdf-proof venv).
```bash
# List all pages and their known drawing types
~/.claude/skills/pdf-proof/.venv/bin/python3 \
~/.claude/skills/freecad-review/scripts/rasterize_pdf_page.py \
--pdf /path/to/cd_set.pdf --page 1 --out /dev/null --info
# Extract page 9 (casework elevations, 1/2" scale) at 2000px width
~/.claude/skills/pdf-proof/.venv/bin/python3 \
~/.claude/skills/freecad-review/scripts/rasterize_pdf_page.py \
--pdf /path/to/cd_set.pdf --page 9 --out /tmp/cd_page9.png --width 2000
# Extract with crop (x0 y0 x1 y1 in PDF points) to isolate one drawing
~/.claude/skills/pdf-proof/.venv/bin/python3 \
~/.claude/skills/freecad-review/scripts/rasterize_pdf_page.py \
--pdf /path/to/cd_set.pdf --page 9 --out /tmp/cd_north_elev.png \
--crop 100 100 1200 800 --width 2000
```
**711 N60th CD set scale map (verified 2026-05-10):**
| Page | Drawing | Scale | vs BIM SVGs |
|------|---------|-------|-------------|
| 4 | Level 1 Floor Plan | 1/4" = 1'-0" | BIM floor plan is 1/2" — CD needs 2× upscale for pixel-diff |
| 8 | Kitchen Interior Elevations | 1/4" = 1'-0" | BIM elevations are 1/2" — CD needs 2× upscale |
| 9 | Kitchen Casework Elevations | **1/2" = 1'-0"** | ✅ Same scale — direct comparison valid |
| 10 | Kitchen Casework Elevations | **1/2" = 1'-0"** | ✅ Same scale — direct comparison valid |
**CD set location:** `/home/tstapler/Documents/711-N60th-Plans/260417-CD_SET_OWNER_REVIEW.pdf`
## Workflow: CD Set vs BIM Comparison
For comparing specific CD set pages against BIM SVG output:
```
1. Extract CD page:
rasterize_pdf_page.py --pdf cd_set.pdf --page 9 --out /tmp/cd_p9.png --width 2000
2. Rasterize BIM SVG:
rsvg-convert -w 2000 kitchen_elev_north.svg -o /tmp/bim_north.png
3. Extract SVG labels from BIM drawing:
python3 -c "import xml.etree.ElementTree as ET; ..."
4. Run Layer 3 dual-pass on each PNG independently → two inventory JSONs
5. Layer 3 Pass 2: compare the two inventories for changes
6. Optional pixel-diff (Pillow — only useful if same scale):
from PIL import Image, ImageChops
diff = ImageChops.difference(Image.open('/tmp/cd_p9.png'), Image.open('/tmp/bim_north.png'))
```
**Scale normalization for pixel-diff (page 4 or 8 vs BIM):**
```python
from PIL import Image
cd = Image.open('/tmp/cd_page8.png')
# CD is 1/4" scale, BIM is 1/2" — double the CD image to match BIM scale
cd_scaled = cd.resize((cd.width * 2, cd.height * 2), Image.LANCZOS)
cd_scaled.save('/tmp/cd_page8_scaled.png')
```
## Project File Locations
For the 711 N60th kitchen project:
- **Annotated floor plan SVG:** `/home/tstapler/Documents/711-N60th-Plans/output/kitchen/kitchen_floor_plan_annotated.svg`
- **Annotated floor plan DXF:** `/home/tstapler/Documents/711-N60th-Plans/output/kitchen/kitchen_floor_plan_annotated.dxf`
- **Plan view SVG:** `/home/tstapler/Documents/711-N60th-Plans/output/plans/kitchen_plan_view.svg`
- **Elevation SVGs + PNGs:** `/home/tstapler/Documents/711-N60th-Plans/output/kitchen/kitchen_elev_*.{svg,png}`
- **Source script:** `/home/tstapler/Documents/711-N60th-Plans/kitchen_permit_docs.py`
- **CD set PDF:** `/home/tstapler/Documents/711-N60th-Plans/260417-CD_SET_OWNER_REVIEW.pdf`
SVG coordinate constants (hardcoded in scripts, verified 2026-05-10):
- `SVG_PER_INCH = 3.306` — SVG units per real inch (from fridge 158.7 SVG = 48")
- `KIT_SVG_X0 = 297.1` — interior west wall SVG x
- `KIT_SVG_Y0 = 166.0` — interior north wall SVG y
- Kitchen interior: 159.0" EW × 125.0" NS
---
## Related Skills
- `freecad-bim-expert` — FreeCAD modeling help, IFC export, BIM structure
- `pdf-proof` — Highlight and verify specific values in PDFs (visual proof pages)
## References
- Research synthesis: `711-N60th-Plans/research/synthesis.md`
- Technique evaluation: `logseq/pages/eval-floor-plan-visual-validation-techniques.md`
- [Claude Vision API Docs](https://platform.claude.com/docs/en/build-with-claude/vision)
- [Text2BIM Paper](https://arxiv.org/html/2408.08054v1) — LLM multi-agent BIM framework with rule-based validation loop
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!