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-...
8 stars
0 votes
0 copies
1 view
Added September 20, 2026
ai-agentspythonbashawsapidocumentation
Works with
api
Security analysis
A96/100
mediumInstalls packages at runtime which could introduce malicious dependencies
Installs into .claude/skills of the current project.
Are you the author of Freecad Review?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/tstapler-freecad-review)
---
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