Install
$ agentstack add skill-impertio-studio-blender-bonsai-ifcopenshell-sverchok-claude-skill-package-bonsai-agents-ifc-validator ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ● Filesystem access Used
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Bonsai IFC Validator Agent
> Version: Bonsai v0.8.x | IfcOpenShell v0.8+ | Blender 4.2.0+ | Python 3.11 > IFC Schemas: IFC2X3, IFC4, IFC4X3 > Dependencies: bonsai-core-architecture, bonsai-syntax-properties, ifcos-impl-validation
Quick Reference: When to Activate
Activate this validator when the user:
- Requests validation, auditing, checking, or verification of an IFC model
- Asks to run quality assurance (QA) on a Bonsai project
- Needs to verify IFC compliance before model handover or delivery
- Wants to check an IFC model against an IDS specification
- Asks to diagnose structural or data issues in an IFC file
- Requests a pre-submission or pre-export quality check
- Needs to verify spatial hierarchy, property completeness, or classification correctness
Critical Warnings
- ALWAYS run validation in the prescribed order: Schema → Spatial → Properties → Geometry → Classification → IDS. Schema failures invalidate all subsequent checks.
- ALWAYS check
model.schemabefore applying version-specific validation logic. IFC2X3, IFC4, and IFC4X3 have different entity sets and rules. - NEVER assume a model is valid because
ifcopenshell.validate.validate()completes without exception. Validation issues are logged, NOT raised. Usejson_loggerorLogDetectionHandlerto detect issues. - ALWAYS use
ifcopenshell.util.element.get_psets()for property reading. NEVER traverseIsDefinedByrelationships manually. - ALWAYS use
ifctesterfor IDS validation.ifcopenshell.validatechecks schema compliance only — it does NOT check project-specific requirements. - NEVER confuse schema validation with IDS validation. Schema validation verifies IFC file structure per EXPRESS schema. IDS validation verifies IFC data meets project information requirements.
- ALWAYS access the IFC model via
tool.Ifc.get()inside Bonsai orIfcStore.get_file(). NEVER accessIfcStore.filedirectly. - ALWAYS guard against
Nonereturn fromIfcStore.get_file()before running any validation.
Validation Process: Execution Order
Execute these phases sequentially. If Phase 1 (Schema) produces BLOCKER-level issues, STOP and report before continuing.
Phase 1: Schema Validation
Validates IFC file structure against the EXPRESS schema definition.
import ifcopenshell
import ifcopenshell.validate
model = ifcopenshell.open("project.ifc") # or tool.Ifc.get() inside Bonsai
# Step 1: Basic schema validation
json_log = ifcopenshell.validate.json_logger()
ifcopenshell.validate.validate(model, json_log)
schema_errors = [s for s in json_log.statements if s["level"] == "ERROR"]
schema_warnings = [s for s in json_log.statements if s["level"] == "WARNING"]
# Step 2: If basic passes, run EXPRESS WHERE rules (10-100x slower)
if not schema_errors:
json_log_express = ifcopenshell.validate.json_logger()
ifcopenshell.validate.validate(model, json_log_express, express_rules=True)
Severity mapping: | Condition | Severity | |-----------|----------| | Any ERROR in jsonlog.statements | BLOCKER | | Any WARNING in jsonlog.statements | WARNING | | EXPRESS WHERE rule violation | WARNING | | Clean schema validation pass | INFO (proceed) |
Phase 2: Spatial Hierarchy Validation
Verifies the required IFC spatial structure exists and all elements are contained.
import ifcopenshell
import ifcopenshell.util.element
def validate_spatial_hierarchy(model):
errors = []
schema = model.schema
# 2a: Verify IfcProject exists (exactly one)
projects = model.by_type("IfcProject")
if len(projects) != 1:
errors.append(("BLOCKER", f"Expected 1 IfcProject, found {len(projects)}"))
return errors
# 2b: Verify IfcSite exists
sites = model.by_type("IfcSite")
if len(sites) == 0:
errors.append(("BLOCKER", "No IfcSite found"))
# 2c: Verify IfcBuilding exists
buildings = model.by_type("IfcBuilding")
if len(buildings) == 0:
errors.append(("BLOCKER", "No IfcBuilding found"))
# 2d: Verify IfcBuildingStorey exists (standard buildings)
storeys = model.by_type("IfcBuildingStorey")
if len(storeys) == 0 and len(buildings) > 0:
errors.append(("WARNING", "No IfcBuildingStorey found"))
# 2e: Verify spatial decomposition chain
# IFC4X3 allows IfcFacility / IfcFacilityPart as alternatives
if schema == "IFC4X3":
facilities = model.by_type("IfcFacility")
if len(buildings) == 0 and len(facilities) == 0:
errors.append(("BLOCKER", "No IfcBuilding or IfcFacility found"))
# 2f: Check all physical elements have spatial containment
for element in model.by_type("IfcElement"):
container = ifcopenshell.util.element.get_container(element)
if container is None:
errors.append((
"WARNING",
f"#{element.id()} {element.is_a()} '{element.Name}': "
f"Not contained in any spatial element"
))
return errors
Severity mapping: | Condition | Severity | |-----------|----------| | Missing IfcProject or count != 1 | BLOCKER | | Missing IfcSite | BLOCKER | | Missing IfcBuilding (IFC2X3/IFC4) | BLOCKER | | Missing IfcBuilding AND IfcFacility (IFC4X3) | BLOCKER | | Missing IfcBuildingStorey | WARNING | | IfcElement without spatial containment | WARNING | | Spatial hierarchy complete | INFO |
Phase 3: Property Set Compliance
Validates property sets exist and contain required properties.
import ifcopenshell.util.element
def validate_property_compliance(model, requirements):
"""
requirements: dict of {ifc_class: {pset_name: [prop_names]}}
Example: {"IfcWall": {"Pset_WallCommon": ["IsExternal", "FireRating"]}}
"""
errors = []
for ifc_class, pset_reqs in requirements.items():
elements = model.by_type(ifc_class)
for element in elements:
psets = ifcopenshell.util.element.get_psets(element)
for pset_name, required_props in pset_reqs.items():
if pset_name not in psets:
errors.append((
"WARNING",
f"#{element.id()} {element.is_a()} '{element.Name}': "
f"Missing pset '{pset_name}'"
))
continue
for prop in required_props:
val = psets[pset_name].get(prop)
if val is None:
errors.append((
"WARNING",
f"#{element.id()} {element.Name}: "
f"Missing {pset_name}.{prop}"
))
return errors
Standard property requirements by element type:
| IFC Class | Standard Pset | Key Properties | |-----------|--------------|----------------| | IfcWall | Pset_WallCommon | IsExternal, LoadBearing, FireRating | | IfcSlab | Pset_SlabCommon | IsExternal, LoadBearing | | IfcDoor | Pset_DoorCommon | IsExternal, FireRating | | IfcWindow | Pset_WindowCommon | IsExternal, ThermalTransmittance | | IfcBeam | Pset_BeamCommon | LoadBearing | | IfcColumn | Pset_ColumnCommon | LoadBearing | | IfcSpace | Pset_SpaceCommon | IsExternal, GrossPlannedArea |
Severity mapping: | Condition | Severity | |-----------|----------| | Standard pset completely missing on element | WARNING | | Required property missing from existing pset | WARNING | | Property value is empty/null | INFO | | All required properties present | INFO |
Phase 4: Geometry Validation
Checks geometry representations exist and are valid.
def validate_geometry(model):
errors = []
for element in model.by_type("IfcElement"):
# 4a: Check element has a representation
if not element.Representation:
errors.append((
"WARNING",
f"#{element.id()} {element.is_a()} '{element.Name}': "
f"No geometry representation"
))
continue
# 4b: Check representation has items
for rep in element.Representation.Representations:
if not rep.Items or len(rep.Items) == 0:
errors.append((
"WARNING",
f"#{element.id()} {element.Name}: "
f"Empty representation '{rep.RepresentationIdentifier}'"
))
# 4c: Check for placement consistency
for element in model.by_type("IfcElement"):
if element.ObjectPlacement is None:
errors.append((
"INFO",
f"#{element.id()} {element.is_a()} '{element.Name}': "
f"No IfcLocalPlacement"
))
return errors
Severity mapping: | Condition | Severity | |-----------|----------| | IfcElement with no Representation at all | WARNING | | Empty representation (no Items) | WARNING | | Missing IfcLocalPlacement | INFO | | Valid geometry present | INFO |
Phase 5: Classification Validation
Verifies classification references (Uniclass, OmniClass, NL-SfB, etc.) are present and correctly structured.
import ifcopenshell.util.classification
def validate_classifications(model, required_system=None):
errors = []
# 5a: Check classification systems exist
systems = model.by_type("IfcClassification")
if len(systems) == 0:
errors.append(("INFO", "No classification system referenced in model"))
return errors
if required_system:
system_names = [s.Name for s in systems]
if required_system not in system_names:
errors.append((
"WARNING",
f"Required classification system '{required_system}' not found. "
f"Available: {system_names}"
))
# 5b: Check elements have classification references
classified_count = 0
unclassified = []
for element in model.by_type("IfcElement"):
refs = ifcopenshell.util.classification.get_references(element)
if refs:
classified_count += 1
else:
unclassified.append(element)
total = len(model.by_type("IfcElement"))
if unclassified:
errors.append((
"INFO",
f"{len(unclassified)}/{total} elements lack classification references"
))
# 5c: Validate classification reference format
for ref in model.by_type("IfcClassificationReference"):
if not ref.Identification and not ref.ItemReference:
errors.append((
"WARNING",
f"#{ref.id()} IfcClassificationReference: "
f"Missing Identification/ItemReference"
))
return errors
Severity mapping: | Condition | Severity | |-----------|----------| | No classification system in model | INFO | | Required classification system missing | WARNING | | Element lacks classification reference | INFO | | IfcClassificationReference with empty ID | WARNING |
Phase 6: IDS Validation (Information Delivery Specification)
Validates the IFC model against an IDS specification file using ifctester.
import ifctester
import ifctester.ids
import ifctester.reporter
def validate_ids(model, ids_path):
"""Validate model against IDS specification."""
errors = []
# 6a: Load IDS specification
ids = ifctester.open(ids_path)
# 6b: Run validation
ids.validate(model)
# 6c: Collect results per specification
for spec in ids.specifications:
total = spec.total_applicable
passed = spec.total_pass
failed = spec.total_fail
if failed > 0:
severity = "BLOCKER" if spec.minOccurs > 0 else "WARNING"
errors.append((
severity,
f"IDS '{spec.name}': {failed}/{total} elements failed"
))
# 6d: Generate report
reporter = ifctester.reporter.Json(ids)
reporter.report()
report_data = reporter.to_string()
return errors, report_data
Bonsai UI access: BIM > IFC Tester panel provides integrated IDS validation.
Severity mapping: | Condition | Severity | |-----------|----------| | Required IDS specification failed | BLOCKER | | Optional IDS specification failed | WARNING | | All IDS specifications passed | INFO | | No IDS specification provided | INFO (skip phase) |
Decision Tree: Validation Severity Classification
Issue found during validation
|
+--> Does it violate IFC schema rules?
| +--> YES --> BLOCKER (invalid IFC file, model cannot be trusted)
| +--> NO --> continue
|
+--> Does it break spatial hierarchy completeness?
| +--> Missing IfcProject/IfcSite/IfcBuilding? --> BLOCKER
| +--> Missing IfcBuildingStorey? --> WARNING
| +--> Element not spatially contained? --> WARNING
| +--> NO --> continue
|
+--> Does it fail a required IDS specification?
| +--> YES (minOccurs > 0) --> BLOCKER
| +--> YES (optional) --> WARNING
| +--> NO --> continue
|
+--> Does it involve missing required properties?
| +--> Missing standard pset entirely? --> WARNING
| +--> Missing individual property? --> WARNING
| +--> Empty property value? --> INFO
| +--> NO --> continue
|
+--> Does it involve geometry issues?
| +--> No representation at all? --> WARNING
| +--> Empty representation? --> WARNING
| +--> Missing placement? --> INFO
| +--> NO --> continue
|
+--> Does it involve classification?
+--> Required system missing? --> WARNING
+--> Element unclassified? --> INFO
+--> Malformed reference? --> WARNING
Severity Definitions:
| Severity | Meaning | Action | |----------|---------|--------| | BLOCKER | Model is invalid or non-compliant. Delivery MUST NOT proceed. | Fix immediately. Re-validate after fix. | | WARNING | Data quality issue. Model is technically valid but incomplete. | Fix before delivery if requirements demand it. | | INFO | Observation. No action required unless project rules specify otherwise. | Document for awareness. |
IDS Integration: ifctester Usage Patterns
Core Workflow
import ifctester
import ifctester.ids
import ifctester.reporter
import ifcopenshell
# Load IDS and IFC
ids = ifctester.open("specification.ids")
model = ifcopenshell.open("project.ifc")
# Validate and report
ids.validate(model)
ifctester.reporter.Console(ids).report()
Report Formats
| Format | Reporter Class | Use Case | |--------|---------------|----------| | Console | ifctester.reporter.Console(ids) | Quick interactive checks | | JSON | ifctester.reporter.Json(ids) | CI/CD pipelines | | HTML | ifctester.reporter.Html(ids) | Stakeholder reports | | BCF | ifctester.reporter.Bcf(ids) | BIM coordination | | ODS | ifctester.reporter.Ods(ids) | Spreadsheet review |
IDS Facet Types
| Facet | Checks | |-------|--------| | Entity | IFC class and predefined type | | Attribute | Entity attribute values | | Classification | Classification references present | | Property | Property set values | | Material | Material assignments | | PartOf | Spatial/aggregation relationships |
Model Access
| Context | Access Method | |---------|--------------| | Inside Bonsai | model = tool.Ifc.get() — ALWAYS check for None | | Inside Bonsai (legacy) | model = IfcStore.get_file() | | Standalone/headless | model = ifcopenshell.open("project.ifc") | | Bonsai UI IDS | BIM > IFC Tester panel |
Version-Specific Validation Notes
| Aspect | IFC2X3 | IFC4 | IFC4X3 | |--------|--------|------|--------| | Spatial root | IfcProject → IfcSite → IfcBuilding | Same as IFC2X3 | IfcProject → IfcSite → IfcBuilding OR IfcFacility | | Georeferencing | Property sets on IfcProject | IfcMapConversion + IfcProjectedCRS entities | Same as IFC4 + IfcMapConversionScaled | | Classification | IfcClassificationReference.ItemReference | IfcClassificationReference.Identification | Same as
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Impertio-Studio
- Source: Impertio-Studio/Blender-Bonsai-ifcOpenshell-Sverchok-Claude-Skill-Package
- License: MIT
- Homepage: https://github.com/OpenAEC-Foundation
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.