Assembly Builder SRS — G-7 Layer-by-Layer TACK¶
Foundation: BBC · DATA_MODEL · BIM_COBOL · MANIFESTO · TestArchitecture
Version: 1.0 (2026-03-19, session 35) Depends on: BIM_Designer.md §18.2 Principle 4, §18.5 MAKE path; BIM_Designer_SRS.md §11 material_layers, §14 InferenceEngine; BOMBasedCompilation.md §4 tack convention; DATA_MODEL.md m_bom + m_bom_line schema Scope: The Assembly MAKE path — stacking existing BUY items into sub-BOMs with layer composition, U-value calculation, and template save/recall. Gate: G-7 (depends on G-4 Save/Recall, feeds G-9 ORDER View + G-10 Promote)
Pattern source: BIMsmith Forge (300K materials, layer-by-layer assembly). Our equivalent uses the same
m_bom+m_bom_lineschema that drives the entire pipeline — assembly templates are data, not code.Rules are DATA, not code. Assembly templates, material properties, and layer compositions live in
component_library.dbtables. Adding a new wall type = SQL INSERT intomaterial_layers. Adding a new assembly = SQL INSERT intom_bom+m_bom_line.
1. Assembly Types — What Can Be Assembled¶
Three categories of assemblies map to construction practice:
| Category | Example | material_layers rows | m_bom_line rows | U-value? |
|---|---|---|---|---|
| WALL | Basic Wall:Exterior - Brick on Block |
6 layers | 6 BOM lines | YES |
| ROOF | Basic Roof:Live Roof over Wood Joist Flat Roof |
6 layers | 6 BOM lines | YES |
| FLOOR | Floor:Residential - Wood Joist with Subflooring |
2 layers | 2 BOM lines | YES |
Current seed data: 20 distinct layer_set_name values in material_layers
(60 rows total): 9 walls, 2 roofs, 2 ceilings, 7 floors.
Additional assembly types (CEILING, STAIR) follow the same pattern but may
not have material_layers rows — they use m_bom_line children directly.
2. Data Model¶
2.1 Assembly Template = m_bom + m_bom_line¶
An assembly template is an m_bom record whose children (m_bom_line) are the
layers. The m_bom itself has no component_type column — the component_type
lives on each m_bom_line child. Each layer line has:
| m_bom_line column | Assembly meaning | Example |
|---|---|---|
child_product_id |
Material product (FK → M_Product) | BRICK_110 |
sequence |
Layer order (outside → inside) | 0, 1, 2, 3 |
role |
Layer function | CLADDING, CAVITY, INSULATION, LINING |
allocated_width_mm |
Layer thickness (= wall-normal direction) | 110 |
allocated_depth_mm |
Same as parent depth (wall length) | parent |
allocated_height_mm |
Same as parent height (wall height) | parent |
component_type |
BUY (leaf material) or MAKE (sub-assembly) | BUY |
Constraint: SUM(allocated_width_mm) across all layers = parent
m_bom.aabb_width_mm. This is the thickness invariant.
2.2 material_layers → Assembly Template Mapping¶
material_layers in component_library.db provides the seed data:
-- material_layers schema
layer_set_name TEXT -- e.g. "Basic Wall:Exterior - Brick on Block"
sequence INTEGER -- 0-based layer order
material_name TEXT -- e.g. "Masonry - Brick"
thickness_m REAL -- layer thickness in metres (SH) or mm (TE)
is_ventilated INTEGER -- 1 if air gap / ventilated cavity
Unit ambiguity (known): The thickness_m column contains mixed units —
metres for some templates, mm for others, even within the same prefix:
- Basic Wall:Exterior - Brick on Block → 0.092 (metres)
- Basic Wall:Wall-Partn_12P-70MStd-12P → 12.5 (mm)
- Basic Roof:Live Roof over Wood Joist Flat Roof → 0.064 (metres)
- Basic Roof:Roof_Flat-4Felt-150Ins-50Scr-150Conc-12Plr → 4.0 (mm)
Prefix is unreliable. The DAO uses magnitude check as primary strategy:
- If MAX(thickness_m) for a layer_set < 1.0 → values are in metres → multiply by 1000
- If MAX(thickness_m) for a layer_set ≥ 1.0 → values are already in mm
All thicknesses normalised to mm on read.
2.3 Material Thermal Properties (new table)¶
U-value calculation requires thermal conductivity per material. New table:
CREATE TABLE IF NOT EXISTS ad_material_thermal (
material_name TEXT PRIMARY KEY, -- FK-ish to material_layers.material_name
conductivity_w_mk REAL NOT NULL, -- λ (W/m·K)
description TEXT, -- human label
source TEXT DEFAULT 'CIBSE' -- data source reference
);
Seed data (29 materials from material_layers, standard values):
| Material | λ (W/m·K) | Source |
|---|---|---|
| Masonry - Brick | 0.77 | CIBSE Guide A |
| Masonry - Concrete Block | 1.13 | CIBSE Guide A |
| Misc. Air Layers - Air Space | 0.56† | CIBSE (R=0.18 at 25mm) |
| Air | 0.56† | CIBSE (R=0.18 at 25mm) |
| Insulation / Thermal Barriers - Rigid insulation | 0.025 | CIBSE Guide A |
| Rigid insulation | 0.025 | CIBSE Guide A |
| Insulation / Thermal Barriers - Semi-rigid insulation | 0.038 | CIBSE Guide A |
| Metal - Stud Layer | 50.0‡ | (bridged: effective R from stud fraction) |
| Metal Stud Layer | 50.0‡ | (bridged) |
| Plasterboard | 0.21 | CIBSE Guide A |
| Plaster | 0.57 | CIBSE Guide A |
| Gypsum Wall Board | 0.21 | CIBSE Guide A |
| Concrete - Cast In Situ | 1.40 | CIBSE Guide A |
| Concrete, Cast In Situ | 1.40 | CIBSE Guide A |
| Concrete | 1.40 | CIBSE Guide A |
| Concrete, Sand/Cement Screed | 1.40 | CIBSE Guide A |
| Concrete, Precast | 1.40 | CIBSE Guide A |
| Concrete Masonry, Floor Block | 1.13 | CIBSE Guide A |
| Wood - Sheathing - plywood | 0.13 | CIBSE Guide A |
| Wood - Dimensional Lumber | 0.13 | CIBSE Guide A |
| Wood - Flooring | 0.14 | CIBSE Guide A |
| Ceramic Tile | 1.30 | CIBSE Guide A |
| Masonry - Grout | 1.40 | (≈ cement) |
| Roofing Felt | 0.19 | CIBSE Guide A |
| Roofing - Barrier | 0.50 | (generic membrane) |
| Roofing - EPDM Membrane | 0.25 | Manufacturer typical |
| Vapor Retarder | 0.50 | (generic membrane) |
| Damp-proofing | 0.50 | (generic membrane) |
| Site - Grass | 0.50 | (soil/turf approximation) |
† Air gaps: thermal resistance is fixed (R=0.18 m²K/W for 25mm unventilated cavity per BS EN ISO 6946). The DAO returns R directly, not λ. ‡ Metal studs: effective conductivity depends on stud fraction. Simplified: use bridged R-value based on typical 15% stud area fraction.
3. API Signatures¶
3.1 New DesignerAPI Methods¶
// ── Assembly Builder (§18.2 Principle 4, G-7) ────────────────────────
/**
* List available assembly templates for a given category (WALL/ROOF/FLOOR).
* Returns templates with their layer stacks and U-values.
*
* // Implementing BIM_Designer.md §18.2 Principle 4 — Witness: W-ASM-LIST-1
*/
AssemblyListResponse listAssemblyTemplates(String category);
/**
* Get the full layer stack for an assembly template.
* Returns layers with materials, thicknesses, and thermal properties.
*
* // Implementing BIM_Designer.md §18.2 Principle 4 — Witness: W-ASM-DETAIL-1
*/
AssemblyDetailResponse getAssemblyDetail(String layerSetName);
/**
* Browse compatible alternative materials for a specific layer position.
* Filters by role compatibility and returns materials with thermal data.
* Returns its own response type (not BrowseItemsResponse — different shape).
*
* // Implementing BIM_Designer.md §18.2 Principle 4 — Witness: W-ASM-BROWSE-1
*/
BrowseAssemblyLayersResponse browseAssemblyLayers(BrowseAssemblyLayersRequest request);
/**
* Replace a layer in an assembly with an alternative material.
* Operates on an in-memory copy — does NOT mutate component_library.db.
* Returns a new AssemblyDetailResponse with recalculated totals.
* The caller decides whether to save (via saveAssemblyTemplate or save()).
*
* // Implementing BIM_Designer.md §18.2 Principle 4 — Witness: W-ASM-SWAP-1
*/
AssemblyDetailResponse swapLayer(SwapLayerRequest request);
/**
* Save an assembly as a reusable template (new m_bom + m_bom_line).
* Per-instance saves go through existing save() → C_OrderLine path.
*
* // Implementing BIM_Designer.md §18.2 Principle 4 — Witness: W-ASM-SAVE-1
*/
AssemblySaveResponse saveAssemblyTemplate(SaveAssemblyRequest request);
3.2 New Records¶
// ── Assembly Builder records ─────────────────────────────────────────
/** Request to browse compatible layers for a position in an assembly. */
record BrowseAssemblyLayersRequest(
String layerSetName, // current assembly template
int layerSequence, // which layer position to browse alternatives for
String materialCategory, // optional filter: "Insulation", "Masonry", etc.
int offset,
int limit
) {}
/** Request to swap a layer in an assembly. */
record SwapLayerRequest(
String layerSetName, // assembly template
int layerSequence, // which layer to replace
String newMaterialName, // replacement material
double newThicknessMm // replacement thickness
) {}
/** Request to save an assembly as a template. */
record SaveAssemblyRequest(
String templateName, // human name for the template
String category, // WALL / ROOF / FLOOR
List<AssemblyLayer> layers // the layer stack
) {}
/** A single layer in an assembly. */
record AssemblyLayer(
int sequence, // 0-based order (outside → inside)
String materialName, // material label
double thicknessMm, // layer thickness in mm
String role, // CLADDING / CAVITY / INSULATION / LINING / STRUCTURE
boolean isVentilated // air gap flag
) {}
/** Response for browsing layer alternatives. */
record BrowseAssemblyLayersResponse(
boolean success,
String layerSetName,
int layerSequence,
List<AlternativeMaterial> alternatives,
String error
) {}
/** A material alternative for a layer position. */
record AlternativeMaterial(
String materialName,
double conductivityWmK,
double defaultThicknessMm,
String roleCompatibility // CLADDING / INSULATION / etc.
) {}
/** Response listing available assembly templates. */
record AssemblyListResponse(
boolean success,
List<AssemblyTemplateSummary> templates,
String error
) {}
/** Summary of one assembly template. */
record AssemblyTemplateSummary(
String layerSetName, // unique ID (= material_layers.layer_set_name)
String category, // WALL / ROOF / FLOOR / CEILING
int layerCount,
double totalThicknessMm,
double uValueWm2K, // pre-calculated
String compliance // UBBL reference or null
) {}
/** Full detail of an assembly template. */
record AssemblyDetailResponse(
boolean success,
String layerSetName,
String category,
List<AssemblyLayer> layers,
double totalThicknessMm,
double uValueWm2K,
String complianceNote, // e.g. "UBBL 2012: max 0.60 W/m²K for ext wall"
String error
) {}
/** Result of saving an assembly template. */
record AssemblySaveResponse(
boolean success,
String bomId, // generated m_bom.bom_id
int layerCount,
String error
) {}
4. U-Value Calculation¶
4.1 Formula (BS EN ISO 6946)¶
U = 1 / R_total
R_total = R_si + Σ(R_layer) + R_se
R_layer = thickness_m / conductivity_w_mk (for solid layers)
R_layer = min(0.18, 0.18 × gap_mm / 25.0) (for air cavities; saturates at 25mm)
R_si = 0.13 (internal surface resistance, horizontal heat flow)
R_se = 0.04 (external surface resistance)
For vertical elements (walls): R_si=0.13, R_se=0.04 For horizontal elements (roofs, upward): R_si=0.10, R_se=0.04 For horizontal elements (floors, downward): R_si=0.17, R_se=0.04
4.2 Metal Stud Bridging (simplified)¶
Metal stud layers use proportional-area method (BS EN ISO 6946 §6.2):
R_bridged = 1 / (f_metal/R_metal + f_insulation/R_insulation)
f_metal = 0.15 (typical 15% stud fraction for 600mm centres, 45mm studs)
f_insulation = 0.85
R_metal = thickness / 50.0 (steel λ ≈ 50 W/m·K)
R_insulation = thickness / 0.038 (cavity fill assumption)
4.3 Compliance Thresholds¶
From UBBL 2012 (Malaysian Uniform Building By-Laws) and common standards:
| Element | UBBL 2012 max U | UK Part L max U | Notes |
|---|---|---|---|
| External wall | 0.60 W/m²K | 0.26 W/m²K | UBBL is tropical, less insulation |
| Roof | 0.40 W/m²K | 0.16 W/m²K | |
| Ground floor | 0.60 W/m²K | 0.18 W/m²K |
Compliance check uses AD_Val_Rule rows with check_method = 'U_VALUE'
and jurisdiction-specific thresholds. This is Phase 2 — G-7 delivers the
calculator; AD_Val_Rule integration comes with G-9/G-10 governance.
4.4 Implementation: UValueCalculator¶
/**
* Stateless U-value calculator. Pure function: layers in → U-value out.
* No DB access — caller provides layers and thermal properties.
*
* // Implementing ASSEMBLY_BUILDER_SRS.md §4 — Witness: W-ASM-UVAL-1
*/
public final class UValueCalculator {
/** Surface resistance by element orientation. */
public enum Orientation { VERTICAL, HORIZONTAL_UP, HORIZONTAL_DOWN }
/**
* Calculate U-value for a layer stack.
*
* @param layers ordered layers (outside → inside)
* @param orientation element orientation for surface resistance
* @return U-value in W/m²K, rounded to 2 decimal places
* @throws IllegalArgumentException if layers is empty
*/
public static double calculate(List<LayerThermal> layers, Orientation orientation);
/** Input record — one layer's thermal data. */
public record LayerThermal(
double thicknessMm,
double conductivityWmK, // λ; ignored if isAirGap=true
boolean isAirGap, // use fixed R instead of λ
boolean isMetalStud // use bridged R calculation
) {}
}
5. DAO Layer¶
5.1 AssemblyDAO (new)¶
Reads component_library.db for assembly templates and material properties.
Read-only — never writes to component_library.db.
/**
* DAO for assembly templates and material thermal data.
* Reads component_library.db. Never writes.
*
* // Implementing ASSEMBLY_BUILDER_SRS.md §5 — Witness: W-ASM-DAO-1
*/
public class AssemblyDAO {
/**
* Load all layer sets for a category prefix.
* @param categoryPrefix "Basic Wall" / "Basic Roof" / "Floor" / "Compound Ceiling"
* @return list of layer set names matching the prefix
*/
List<String> listLayerSets(String categoryPrefix);
/**
* Load full layer stack for a layer set.
* Normalises thickness to mm.
* @return ordered list of layers (by sequence)
*/
List<MaterialLayer> loadLayers(String layerSetName);
/**
* Load thermal conductivity for a material.
* @return conductivity in W/m·K, or null if unknown
*/
Double getThermalConductivity(String materialName);
/**
* Load all thermal properties (batch, for U-value calc).
* @return map of material_name → conductivity_w_mk
*/
Map<String, Double> loadAllThermalProperties();
/**
* Browse compatible alternative materials for a layer position.
* Filters by role compatibility (e.g., insulation slot → insulation materials).
* @param currentMaterial the material being replaced
* @param role layer role (CLADDING, INSULATION, etc.)
* @return compatible M_Product entries
*/
List<AlternativeMaterial> browseAlternatives(String currentMaterial, String role);
/** Raw layer from material_layers table. */
record MaterialLayer(
String layerSetName,
int sequence,
String materialName,
double thicknessMm, // normalised to mm
boolean isVentilated
) {}
/** An alternative material for a layer position. */
record AlternativeMaterial(
String materialName,
double conductivityWmK,
String description
) {}
}
5.2 AssemblyWriteDAO (new)¶
Writes assembly templates to output.db (per-instance) or to a new BOM file (reusable template).
/**
* Write DAO for assembly templates.
* Per-instance: writes to output.db C_OrderLine.
* Reusable: writes to {PREFIX}_BOM.db m_bom + m_bom_line (via Promote path).
*
* // Implementing ASSEMBLY_BUILDER_SRS.md §5.2 — Witness: W-ASM-WRITE-1
*/
public class AssemblyWriteDAO {
/**
* Save assembly as per-instance OrderLine data.
* Each layer → one C_OrderLine child under the wall/roof/floor OrderLine.
* @return number of lines written
*/
int savePerInstance(Connection conn, String parentOrderLineId,
List<AssemblyLayer> layers);
/**
* Save assembly as reusable template (m_bom + m_bom_line).
* Calls through to Promote path — requires governance gate.
* @return generated bom_id
*/
String saveAsTemplate(Connection conn, String templateName,
String category, List<AssemblyLayer> layers);
}
6. Wire Protocol — BlenderBridge Extensions¶
6.1 New Verbs (Python ← Java)¶
| Verb | Direction | Payload | Action |
|---|---|---|---|
show_assembly_editor |
Java → Python | {layers, totalMm, uValue} |
Render layer stack panel in Blender N-panel |
update_assembly_layer |
Python → Java | {layerSetName, seq, newMaterial, newThickness} |
swapLayer() → recalculated response |
browse_layer_alternatives |
Python → Java | {layerSetName, seq} |
browseAssemblyLayers() → product list |
save_assembly |
Python → Java | {name, category, layers} |
saveAssemblyTemplate() → bomId |
show_composition |
Python → Java | {wallType} |
Query material_layers → return layer stack |
6.2 Wire Protocol Examples¶
// List templates
{"action":"listAssemblyTemplates", "category":"WALL"}
→ {"templates":[
{"layerSetName":"Basic Wall:Exterior - Brick on Block",
"category":"WALL", "layerCount":6, "totalThicknessMm":417,
"uValueWm2K":0.35},
...
]}
// Get detail
{"action":"getAssemblyDetail", "layerSetName":"Basic Wall:Exterior - Brick on Block"}
→ {"layers":[
{"seq":0, "material":"Masonry - Brick", "thicknessMm":92, "role":"CLADDING"},
{"seq":1, "material":"Misc. Air Layers - Air Space", "thicknessMm":25, "role":"CAVITY"},
{"seq":2, "material":"Insulation / Thermal Barriers - Rigid insulation",
"thicknessMm":50, "role":"INSULATION"},
{"seq":3, "material":"Masonry - Concrete Block", "thicknessMm":193, "role":"STRUCTURE"},
{"seq":4, "material":"Metal - Stud Layer", "thicknessMm":41, "role":"STRUCTURE"},
{"seq":5, "material":"Plasterboard", "thicknessMm":16, "role":"LINING"}
],
"totalThicknessMm":417, "uValueWm2K":0.51}
// Swap a layer
{"action":"swapLayer", "layerSetName":"Basic Wall:Exterior - Brick on Block",
"layerSequence":2, "newMaterial":"Rigid insulation", "newThicknessMm":100}
→ {"layers":[...updated...], "totalThicknessMm":467, "uValueWm2K":0.28}
// Query composition (existing verb from BIM_Designer_SRS.md §12.3)
{"action":"queryComposition", "wallType":"Basic Wall:Exterior - Brick on Block"}
→ {"layers":[...], "total_mm":417}
7. Constraints & Validation¶
7.1 Thickness Invariant¶
ASSERT: SUM(layer.thicknessMm for all layers) == parent.aabb_width_mm
When a layer is swapped, the parent AABB is updated to match the new total. This is a schema update (update m_bom.aabb_width_mm), not a geometry operation. The compiler handles the 3D consequences.
7.2 Layer Compatibility¶
Not every material fits every role. Role compatibility matrix:
| Role | Compatible materials |
|---|---|
| CLADDING | Brick, block, timber cladding, render |
| CAVITY | Air gap (must be is_ventilated=1 or sealed) |
| INSULATION | Rigid, semi-rigid, spray foam, mineral wool |
| STRUCTURE | Block, concrete, timber frame, steel stud |
| LINING | Plasterboard, plaster, gypsum board |
| MEMBRANE | Vapour retarder, damp-proofing, EPDM |
Phase 1: Advisory only — warn on role mismatch, don't block.
Phase 2: Enforce via ad_assembly_manifest.interface_type matching.
7.3 InferenceEngine Integration¶
Layer placement is a constraint satisfaction problem (BIM_Designer_SRS.md §14):
fits(Layer, Slot) :- thickness(Layer, T), max_thickness(Slot, MaxT), T =< MaxT.
compatible(Layer, Slot) :- role(Layer, R), accepts(Slot, R).
valid_stack(Layers) :- thickness_invariant(Layers), all_compatible(Layers).
Phase 1 (G-7): Direct validation in AssemblyBuilderService (no InferenceEngine). Phase 2 (G-9+): Migrate to InferenceEngine rules with proof trees.
8. Witness Claims¶
8.1 Witness Matrix¶
| ID | Witness | What it proves | Acceptance criteria |
|---|---|---|---|
| W-ASM-LIST-1 | ListAssemblyTemplates | listAssemblyTemplates("WALL") returns ≥ 7 wall templates |
count ≥ 7 (9 wall layer_set_names exist) |
| W-ASM-LIST-2 | ListAssemblyTemplates | listAssemblyTemplates("FLOOR") returns ≥ 3 floor templates |
count ≥ 3 |
| W-ASM-LIST-3 | ListAssemblyTemplates | listAssemblyTemplates("ROOF") returns ≥ 1 roof templates |
count ≥ 1 |
| W-ASM-DETAIL-1 | GetAssemblyDetail | Brick-on-Block returns 6 layers, total 417mm | layerCount=6, totalMm=417 |
| W-ASM-DETAIL-2 | GetAssemblyDetail | Layer order matches material_layers.sequence | layers[0].seq=0, layers[5].seq=5 |
| W-ASM-UVAL-1 | UValueCalculator | Brick-on-Block U ≈ 0.37 W/m²K (±0.05) | 0.32 ≤ U ≤ 0.42 |
| W-ASM-UVAL-2 | UValueCalculator | Swap insulation 50→100mm reduces U | U_after < U_before |
| W-ASM-UVAL-3 | UValueCalculator | Empty layers list → IllegalArgumentException | exception thrown |
| W-ASM-UVAL-4 | UValueCalculator | Air gap uses fixed R=0.18, not λ/d | R_airgap = 0.18 regardless of thickness |
| W-ASM-BROWSE-1 | BrowseAssemblyLayers | Browse insulation slot returns insulation-compatible materials | all results have insulation-class conductivity (λ < 0.1) |
| W-ASM-SWAP-1 | SwapLayer | Swap insulation 50→100mm → totalMm increases by 50 | new_total = old_total + 50 |
| W-ASM-SWAP-2 | SwapLayer | Swap recalculates U-value | U_after ≠ U_before |
| W-ASM-SAVE-1 | SaveAssemblyTemplate | Save creates m_bom + m_bom_line | bom_line count = layer count |
| W-ASM-DAO-1 | AssemblyDAO.loadLayers | SH Brick-on-Block normalises metres → mm | all thicknessMm > 1.0 |
| W-ASM-DAO-2 | AssemblyDAO.loadLayers | TE-style data (already mm) stays mm | no double-conversion |
| W-ASM-WRITE-1 | AssemblyWriteDAO | Per-instance save creates C_OrderLine children | child count = layer count |
| W-ASM-THICK-1 | ThicknessInvariant | SUM(layers) = parent total for all 20 templates | all 20 pass |
8.2 Non-Disturbance¶
G-7 must not disturb existing gates:
| Gate | Guard |
|---|---|
| G1-COUNT | No changes to extraction pipeline |
| G2-VOLUME | No changes to verb geometry |
| G3-DIGEST | Seal set unchanged |
| G4-TAMPER | No AD_Val_Rule changes |
| G5-PROVENANCE | No GEO_ prefixes introduced |
| G6-ISOLATION | No cross-stone contamination |
Test strategy: Run ./scripts/run_RosettaStones.sh after implementation.
All existing 136/136 must remain GREEN.
9. Implementation Plan¶
Phase 1: Core (this session)¶
| Step | File | What |
|---|---|---|
| 1 | migration/027_ad_material_thermal.sql |
Create ad_material_thermal + seed 29 materials |
| 2 | AssemblyDAO.java |
Read material_layers + thermal properties |
| 3 | UValueCalculator.java |
Stateless calculator (§4 formula) |
| 4 | AssemblyBuilderService.java |
Orchestration: list/detail/swap/save |
| 5 | DesignerAPI.java |
Add 5 new method signatures |
| 6 | DesignerAPIImpl.java |
Wire to AssemblyBuilderService |
| 7 | AssemblyBuilderTest.java |
17 witnesses (W-ASM-*) |
Phase 2: Wire + Governance (future session)¶
| Step | File | What |
|---|---|---|
| 8 | BlenderBridge verbs | show_assembly_editor + update_assembly_layer |
| 9 | AD_Val_Rule rows | U_VALUE check_method + jurisdiction thresholds |
| 10 | InferenceEngine | Layer constraint rules with proof trees |
File Locations¶
BonsaiBIMDesigner/src/main/java/com/bim/designer/
├── api/DesignerAPI.java (add records + methods)
├── api/DesignerAPIImpl.java (wire to service)
├── assembly/AssemblyBuilderService.java (NEW — orchestration)
├── assembly/AssemblyDAO.java (NEW — read component_library.db)
├── assembly/AssemblyWriteDAO.java (NEW — write output.db)
├── assembly/UValueCalculator.java (NEW — stateless calc)
BonsaiBIMDesigner/src/test/java/com/bim/designer/
├── AssemblyBuilderTest.java (NEW — 17 witnesses)
migration/
├── 027_ad_material_thermal.sql (NEW — thermal properties table + seed)
10. Open Questions¶
| # | Question | Default assumption | Blocks |
|---|---|---|---|
| Q1 | Should metal stud bridging use simplified 15% or actual stud dims? | 15% simplified | W-ASM-UVAL-1 tolerance |
| Q2 | Should browse alternatives query M_Product or just material_layers? | material_layers (simpler, no product mapping needed yet) | W-ASM-BROWSE-1 |
| Q3 | Should save-as-template go directly to BOM.db or through Promote? | Through Promote (existing governance path) | W-ASM-SAVE-1 |
| Q4 | Unit normalisation: trust layer_set_name prefix or magnitude check? | Prefix first, magnitude fallback | W-ASM-DAO-1 |
Spec citation: BIM_Designer.md §18.2 Principle 4 + §18.5 MAKE path. Traceability: TestArchitecture.md §Traceability Matrix — G-7 row to be added after implementation.