# Part II. Language

This part defines the records. Each record kind has a JSON Schema in [`schemas/`](https://gitlab.com/openeik/spec/-/tree/main/schemas); the schema fixes the members and their types, and this text fixes what they mean and the rules a schema cannot state. A record MUST validate against its schema, and MUST meet the rules of this part.

## 1. The record kinds

| Kind | One per | Stored at (Part III) | Schema | Group |
|---|---|---|---|---|
| Project | model | `project/project.json` | `project/project.schema.json` | core |
| Levels | model (one list) | `project/levels.json` | `project/levels.schema.json` | core |
| Order | model | `project/order.json` | `project/order.schema.json` | core |
| Grid | grid line | `project/grids/<id>.json` | `project/grid.schema.json` | core |
| Material | material | `project/materials/<id>.json` | `project/material.schema.json` | core |
| Type | type | `types/<discipline>/<category>/<id>.json` | `types/<category>.schema.json` | core |
| Element | element | `<discipline>/<level>/<category>/<id>.json` | `elements/<category>.schema.json` | core and domain profiles |
| Original | placed group | `project/originals/<id>.json` | `project/original.schema.json` | core |
| Copy | placement of a group | `project/copies/<id>.json` | `project/copy.schema.json` | core |
| View | view | `drawings/<discipline>/views/<kind>/<id>/view.json` | `drawings/view-record.schema.json` (`drawings/view.schema.json` with its items inline) | documentation profile |
| View detail chunk | up to 256 detail items | `drawings/<discipline>/views/<kind>/<id>/details/<n>.json` | `drawings/view-details.schema.json` | documentation profile |
| Scope box | scope box | `project/scope-boxes/<id>.json` | `project/scope-box.schema.json` | documentation profile |
| Dimension style | style | `project/dimension-styles/<id>.json` | `project/dimension-style.schema.json` | documentation profile |
| Detail family | 2D family | `project/detail-families/<id>.json` | `project/detail-family.schema.json` | documentation profile |
| Piece | fabricated unit | `fabrication/pieces/<id>.json` | `fabrication/piece.schema.json` | fabrication profile |
| Joint | structural joint | `fabrication/joints/<id>.json` | `fabrication/joint.schema.json` | fabrication profile |
| Sheet, title block, image, report, render, deviation, area adjustment, linked reference | record | Part III, section 3 | `application-record.schema.json` | application records |

The order is listed for its file and its schema: it is an index of the record lists, not a record, and the single-file form has none (Part III, 9, rule 5, and 11).

Reference: `manifestSchema` in `src/core/model/model.ts` (the lists and their record schemas); `recordLists` and `listFolders` in `src/core/versions/layout.ts`.

## 2. The project

`project/project.json` holds:

- `schemaVersion`: `1` (Part I, 9.3);
- `project`: `{id, name, units: "mm", upAxis: "Z"}` and optionally `elevationOffset` (the project's zero above a shared datum, in mm), `site` (latitude, longitude, true north in degrees clockwise from plan +Y, an IANA time zone), `rooms` (room measuring rules), `excavation` and `precast` (rules for those profiles), `standards` (drawing standards: an application value) and `envelope` (the envelope's air leakage and thermal bridges, section 2.1);
- `projectInformation` (optional): values shown on drawings.

A model SHOULD give its `site` with `trueNorth`: without it, plan +Y is taken as north (section 6.8), which is right only by chance, and an energy balance cannot place the sun. In 0.1 a `site` gives all four of its members, so a writer that knows true north but not where the site lies cannot give it; from 0.2, `trueNorth` may be given alone (decided on 22 October 2026; open questions, 44), as section 2.2 says.

A `site`'s latitude and longitude name the plan's origin: the point `[0, 0]` of every profile, at the project's zero, the height `0` that levels' elevations are measured from (section 3), which lies `elevationOffset` above the shared datum. True north lies `trueNorth` degrees clockwise from the plan's +Y about that point (section 6.8). 0.1 names no coordinate reference system, the map grid in which a surveyor gives eastings and northings, nor a vertical datum: a bridge that places the plan on such a grid takes them from its caller (Part VIII, 1). By the steward's decision of 26 October 2026 (open questions, 79), 0.2 adds an optional `site.crs` that names both by their EPSG codes, as section 2.2 says.

*Note (informative).* The reference implementation reads the latitude and the longitude so: its IFC export places the plan's origin at them, projected into SWEREF 99 TM, and turns the plan's x axis by true north and the grid's convergence (`eastNorth` and `gridConvergence` in `src/core/site/geo.ts`; `exportIfc` in `src/core/io/ifcExport.ts`). It has no base point or survey point of its own.

`project.id` is the project's identity. Like every id it is stable for the life of the project (Part I, 6): every version, alternative and copy of the folder keeps it. IFC GlobalIds are derived from it (Part VIII), so an element keeps its GlobalId across them.

### 2.1 The envelope

`envelope` is `{airLeakage?, thermalBridges?}`, the values of the thermal envelope as a whole that an energy balance needs and no element carries:

| Member | Unit | Meaning |
|---|---|---|
| `airLeakage` | l/(s·m²) | the envelope's design air leakage at a pressure difference of 50 Pa, q50, per m² of the envelope's area (EN ISO 9972) |
| `thermalBridges` | | a list, at most 1000, of `{kind, psi, name?}`: the linear thermal transmittance `psi` in W/(m·K), from −2 to 5, of the junctions of one kind (EN ISO 14683, EN ISO 10211) |
| `dimensions` | | which dimensions the `psi` values refer to: `internal`, `overall-internal` or `external` (EN ISO 13789). A ψ differs with them, so it SHOULD be given with `thermalBridges` |

The lengths are not stored: a tool measures them from the elements where the junctions are (section 10), in the dimensions `dimensions` names. A thermal bridge's `kind` is a plain string, and two thermal bridges MUST NOT have the same kind: a tool would not know which ψ to take. So that every tool measures the same junctions, a kind SHOULD be one of these names:

| Kind | The junction |
|---|---|
| `wall-ground-slab` | an exterior wall on a slab on the ground |
| `wall-roof` | an exterior wall to a roof |
| `wall-intermediate-floor` | an exterior wall past an intermediate floor |
| `opening-in-wall` | around a window or a door in an exterior wall |
| `outer-corner`, `inner-corner` | where two exterior walls meet, outside or inside |
| `balcony` | a balcony slab through an exterior wall |

A kind may also be a designation of EN ISO 14683 with its number (`GF1`, `W8`), the junction of that standard's catalogue: a tool reads it only if it knows the catalogue. A corner is better named `outer-corner` or `inner-corner`, which says which it is without the catalogue. A tool that does not know a kind SHOULD say so, and not guess its length. The envelope is set with `project.envelope` (Part V, 2.1).

### 2.2 The site

*Part of 0.2. OpenEik 0.1 had it as the proposal `site`: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), a `site` gives all four of its members, and `crs` is a member a schema of 0.1 does not know, which a reader keeps (Part I, 10). The section adds one member, `site.crs`, and no file.*

By the steward's decisions of 22 October 2026, asked by an analysis application (open questions, 44: rules 1 and 2), and of 26 October 2026 (open questions, 79: rules 3 to 5).

1. **True north alone.** A `site` MAY give `trueNorth` alone. Its `latitude`, `longitude` and `timeZone` are its location: a site gives the three together or none of them, and a site that gives its location gives `trueNorth` too. So a site is `{trueNorth}`, or all four. A validator reports a site that gives part of its location, or its location without `trueNorth`, as its schema's error.
2. **A site without a location** says which way north is, and nothing of where the site lies. Its true north is read as section 6.8 reads it. A tool that needs the location, an energy balance that places the sun or a bridge that places the plan on a map, has none, and SHOULD say so rather than assume one. A bridge that writes IFC writes the true north of its context and no latitude, longitude or map conversion.

3. **Its coordinate reference systems.** A site that gives its location MAY give `crs`, `{horizontal, vertical?}`, each the code of a coordinate reference system in the EPSG registry, written `EPSG:` and the number without a leading zero: `horizontal` the projected system, the map grid, in which a surveyor gives eastings and northings, and `vertical` the vertical system in which heights are given. A site MUST NOT give `crs` without its latitude and its longitude; a validator reports it, and a code of another form, as its schema's error. OpenEik lists no systems, and does not check that a code is one the registry gives: its publisher, the registry, does.
4. **What it says.** The latitude and the longitude stay the site's place: the plan's origin `[0, 0]` (above). `crs.horizontal` names the grid a bridge projects that place into, and where the site gives `crs.vertical`, `elevationOffset` is the height of the project's zero in that vertical system. A writer that is given a surveyor's eastings and northings in a grid turns them into a latitude and a longitude, and names the grid in `crs.horizontal`, so that the model holds one place: it holds no eastings and northings beside the latitude and the longitude, which could disagree with them.
5. **A bridge** that places the plan on a map projects the latitude and the longitude into `crs.horizontal`, and turns the plan's x axis by true north and by the grid's convergence there, the angle from true north to the grid's north; Part VIII, 1.7 says how IFC writes it. A bridge that cannot project the system says so in its fidelity report, and writes no map conversion. Which systems a bridge projects is the bridge's: a code it does not know is no error of the model.

A tool that reads a plan drawing with a north arrow, and nothing of where the building stands, writes `"site": {"trueNorth": -12.5}`: north lies 12.5° counter-clockwise from the plan's +Y. A project placed in SWEREF 99 TM, its heights in RH2000, writes `"site": {"crs": {"horizontal": "EPSG:3006", "vertical": "EPSG:5613"}, "latitude": 57.70887, "longitude": 11.97456, "timeZone": "Europe/Stockholm", "trueNorth": 0}`, and `"elevationOffset": 12500` when its zero lies 12.5 m above RH2000's.

*Note (informative).* The reference implementation's schema asks for all four members (`siteSchema` in `src/core/model/model.ts`); by the steward's decision it changes first, before 0.2 (open questions, 44). Its model names no coordinate reference system: its IFC export names SWEREF 99 TM and RH2000 for every project with a site, and projects the latitude and the longitude into it (`eastNorth`, `sweref99tm` and `gridConvergence` in `src/core/site/geo.ts`; `exportIfc` in `src/core/io/ifcExport.ts`).

## 3. Levels

`project/levels.json` is the list of levels in their stored order, at least one. Each is `{id, name, elevation}`. Level ids MUST be unique. Level order in the file is the order a writer keeps; readers MUST NOT infer elevation order from it.

## 4. Materials

A material is `{id, name, color, pattern, revision}` with an optional `category` (section 4.2), from 0.4 an optional `masonry` (section 4.3), an optional `surface` (a projected surface pattern: brick, boards, panels and others) and the optional physical values of section 4.1. `id` matches `^[A-Za-z0-9_-]{1,60}$`.

1. A layer names its material **by name**: a layer's `material` text matches the material whose `name` is equal to it, ignoring case and surrounding white space.
2. A layer whose material text matches no material is valid: its material is that text alone.
3. A layer of a type that has an `insulation` layer SHOULD have a design thermal conductivity: its own (`thermal.lambda`, section 5.1) or its material's `lambda`. A membrane and an air layer (one with `thermal.air`) need none: EN ISO 6946 gives an air layer its own resistance. In 0.1 an insulation layer marks a part of the thermal envelope, since a model does not yet say which parts face the outside.
4. **The cut pattern `clt`** (of 0.2; section 14). A material's `pattern`, the hatch its cut is drawn with, and a layer's own (section 5.1), MAY be `clt`, cross-laminated timber's, beside the patterns of 0.1. A drawing draws it as it draws the others: how a hatch looks is the drawing application's. In 0.1 it is a value the schemas refuse; a validator of 0.2 takes it (open questions, 115).

Reference: `materialNamed` in `src/core/assemblies/materialLibrary.ts` and its use in `src/core/geometry/geometry.ts`.

*Note (informative).* Before 1.0, a layer will name its material by id (`materialId`) rather than by name, as the plan (F06) proposed, with a migration from names (planned; decided 11 October 2026). 0.1 matches by name, as the reference implementation does.

### 4.1 Physical values

The values that building physics, structure, fire and climate tools read from a material, all optional. Where a value comes from a standard's classes, it is a plain string (section 8, rule 3).

| Member | Unit | Meaning |
|---|---|---|
| `lambda` | W/(m·K) | the design thermal conductivity, as a calculation uses it (EN ISO 10456); greater than 0, at most 1000 |
| `density` | kg/m³ | the density ρ of the material itself (EN ISO 10456): plain concrete's, not reinforced concrete's; greater than 0, at most 30 000. It is no structural unit weight (below) |
| `specificHeat` | J/(kg·K) | the specific heat capacity c; greater than 0, at most 20 000 |
| `vapourResistance` | | the water vapour diffusion resistance factor μ (EN ISO 10456, EN ISO 13788); from 1 to 10⁹. A membrane whose thickness is nominal gives its layer's `thermal.sd` instead |
| `solarAbsorptance` | | α of an outer surface, from 0 to 1 |
| `emissivity` | | ε of a surface that faces an air layer, from 0 to 1 (EN ISO 6946, annex D) |
| `lightReflectance` | | the visible light reflectance of an inner surface, from 0 to 1, for daylight |
| `criticalMoisture` | % | the critical relative humidity at which the material risks mould or damage, greater than 0, at most 100: the critical moisture state that a moisture safety design checks against |
| `fireReaction` | | the reaction to fire class (EN 13501-1), such as `A1`, `B-s1,d0` or `Dfl-s1` |
| `strengthClass` | | the strength class or grade (EN 338, EN 14080, EN 206, EN 10025, EN 10080, and for masonry EN 771 and EN 998-2, section 4.2), such as `C24`, `GL30c`, `C30/37`, `S355`, `B500B` or `M10`. A member's section states its own grade, which decides where the two differ (section 5.2, rule 6) |
| `climate` | | `{gwpA1A3, unit, source?}`: the global warming potential of modules A1–A3 in kg CO₂e per declared unit, the unit (`kg`, `m3`, `m2`, `m` or `piece`), and where it comes from (an EPD's identifier, EN 15804, or a climate database's) |

A writer that has a value from a declaration rather than a design value says so in the material's `name` or in an extension member (Part I, 11); `lambda` is the value a calculation uses.

`density` is the material's own density, the ρ that a heat capacity, a moisture calculation or a mass reads. It is no structural unit weight: a self-weight for structural design includes what the material carries as built, such as the reinforcement in concrete, and is a weight per volume, in kN/m³.

*Note (informative).* A structural tool takes the self-weight of a material it recognises from its own source, such as EN 1991-1-1, annex A, which gives normal-weight concrete 24 kN/m³ and adds 1 kN/m³ for its reinforcement. For a material it does not recognise, `density` times g is the best weight the model gives, and the tool says that it used it (decided on 22 October 2026; open questions, 41).

### 4.2 A material's category

*Part of 0.3. OpenEik 0.2 had it as the proposal `materialCategory` (open questions, 121): a model of 0.2, read by 0.2's rules (Part I, 9.6, rule 3), is read by it only where a caller asks for it (decision D12).*

A material says what kind of material it is, so that a tool that analyses, prices, prints or exports a model reads it rather than telling it from a strength class or from words in a name, which differ by language and by writer. In a model of 0.2 that does not ask for the proposal, a material's `category` is a member the schemas of 0.2 do not know, which a reader keeps and a validator may warn about (Part I, 10).

1. **A material's category.** A material MAY carry `category`, one value of rule 2's list. A validator MUST report any other value as an error (`schema-invalid`).
2. **The list.** IFC 4's eleven recommended values of IfcMaterial's Category, as IFC gives them, and seven more:

   | Value | The material |
   |---|---|
   | `concrete` | concrete, cast in place or precast, plain or reinforced |
   | `steel` | steel, carbon or stainless, its sections, plates, sheets and bars |
   | `aluminium` | aluminium and its alloys |
   | `block` | masonry blocks: of concrete, of lightweight aggregate, of aerated concrete |
   | `brick` | bricks, of fired clay or of calcium silicate |
   | `stone` | natural stone |
   | `wood` | timber and every product made from it: sawn, glued laminated, cross-laminated, laminated veneer lumber, and boards of wood such as plywood, OSB and fibreboard |
   | `glass` | glass |
   | `gypsum` | gypsum plaster and plasterboard |
   | `plastic` | plastics, cellular plastics such as EPS, XPS and PIR among them, and plastic membranes |
   | `earth` | soil, clay and fill |
   | `masonry` | masonry whose units are not said: units and mortar as one material |
   | `metal` | a metal other than steel and aluminium, such as copper, zinc and lead |
   | `mineralFibre` | mineral wool, of glass or of stone, and other mineral fibre |
   | `bitumen` | bitumen and bituminous felts and membranes |
   | `ceramic` | ceramic tiles and other fired ceramic that is no brick |
   | `mortar` | mortar and render, of cement, of lime or of both |
   | `other` | a material none of these values names |

3. **Engineered timber is wood.** A glued laminated or cross-laminated timber, or another engineered timber, is `wood`, and no category of its own: its `strengthClass`, such as `GL30c` (EN 14080), or a cut pattern `clt` (section 4, rule 4) says which. A second place that said it could disagree with the first, as a member's grade and its material's strength class can (section 5.2, rule 6).
4. **In IFC.** A bridge writes a material's category as its IfcMaterial's `Category` (Part VIII, 1), the value as rule 2 gives it, and a material without one with no Category. A reader of IFC (Part VIII, 2) reads an IfcMaterial's `Category` that is one of rule 2's values, its case ignored (`Concrete` is `concrete`), as its material's category, and leaves out any other.
5. **The strength classes of masonry.** A material's `strengthClass` (section 4.1) MAY also give a masonry unit's class by EN 771, or a mortar's class by EN 998-2, such as `M10`, a plain string as the other standards' classes are (section 8, rule 3).

Reference: none in the reference implementation, which tells a material's surface from its name (`namedSurface` in `src/core/walk/walkSurfaces.ts`). In `@openeik/tools`: `MATERIAL_CATEGORIES` and `categoryOfIfc` in `packages/openeik/src/material-category.js`; the validator holds a material's `category` to `schemas/0.2/proposed/materialCategory/category.schema.json` by the proposal's index (`proposed03Members` in `check.js`); `ifcOfModel` writes and `eikOfIfc` reads the Category; for a model of 0.3, and for one of 0.2 asked for with `proposals: ["materialCategory"]`.

*Note (informative).* The analysis application asked for a material's kind (open questions, 121): it told concrete, masonry, steel and timber apart by the strength class, the pattern `clt` and words in the name, and a material of masonry by its name alone. The steward decided on 31 October 2026 that 0.3 has a material's category and masonry's strength classes, and this section was written as a proposal for 0.3, which the cut of 0.3 made a rule until the cut of 0.3 makes it a rule. IFC 4 recommends its eleven values without making them a closed list; this section closes the list so that every tool reads the same values, and a file of another tool's values is read for those of the list alone.


### 4.3 Masonry's design values

*Part of 0.4. OpenEik 0.3 had it as the proposal `masonry` (open questions, 127): a model of 0.3, read by 0.3's rules (Part I, 9.6, rule 3), is read by it only where a caller asks for it (decision D12).*

A material of masonry says what a design of masonry by EN 1996-1-1 reads of it, which no strength class says: its units, its mortar, how it is laid, and what tests declare of it. In a model of 0.3 that does not ask for the proposal, a material's `masonry` is a member the schemas of 0.3 do not know, which a reader keeps and a validator may warn about (Part I, 10).

1. **Which materials.** A material whose `category` is `masonry`, `brick`, `block` or `stone` (section 4.2) MAY carry `masonry`, its design values. A validator MUST report `masonry` on a material of another category, or of none, as an error (`masonry-category`). A layer of masonry names one such material, which holds its units and its mortar both.
2. **Its units.** `unit` is `{standard, fb?, group?, category?}`, and required: `standard`, the part of EN 771 its units are made to, one of `EN 771-1` to `EN 771-6`, which says their material (clay, calcium silicate, aggregate concrete, autoclaved aerated concrete, manufactured stone, natural stone); `fb`, their normalised mean compressive strength, in N/mm², more than 0; `group`, 1 to 4, their group by EN 1996-1-1, Table 3.1; and `category`, `I` or `II`, the category of their manufacture's control.
3. **Its mortar.** `mortar`, where given, is `{kind, class?, fm?, designed?}`: `kind`, `general`, `thin` or `light`, a general purpose, thin layer or lightweight mortar (EN 998-2), a thin layer's bed joints being 0.5 to 3 mm thick as EN 1996-1-1 says, so that no member gives their thickness; `class`, its compressive strength class, such as `M10`, a plain string (section 8, rule 3); `fm`, its compressive strength, in N/mm², more than 0; and `designed`, true for a designed mortar and false for a prescribed one (EN 998-2).
4. **How it is laid.** `executionClass`, 1 to 5, the class of execution by which a national annex sets the partial factor; and `joints`, `{perpends, longitudinal}`: `perpends`, `filled` or `unfilled`, the perpend joints (EN 1996-1-1, 3.6.2), and `longitudinal`, true where a joint runs along the wall through its thickness (3.6.1.2).
5. **What tests declare.** `declared`, `{fk?, fvko?, fxk1?, fxk2?, creep?, KE?}`, values a test gives in place of those EN 1996-1-1 derives: the characteristic compressive strength `fk`, the initial shear strength `fvko` and the flexural strengths `fxk1` and `fxk2`, each in N/mm² and more than 0; the final creep coefficient `creep`, 0 or more; and `KE`, more than 0, the factor that gives the modulus of elasticity, E = KE · fk.
6. **What it is not.** `strengthClass` stays a plain string (section 4.1, and 4.2, rule 5), for a model that has only a name. A wall's effective height and the restraint of its edges are the wall's and the analysis's, and no member of a material.

Reference: none in the reference implementation. In `@openeik/tools`: the proposal's schema, `schemas/0.3/proposed/masonry/masonry.schema.json`, which the validator holds a material's `masonry` to by the proposal's index; `masonryChecks` in `packages/openeik/src/check.js` (`masonry-category`); asked for with `proposals: ["masonry"]` on a model of 0.3.

*Note (informative).* The analysis application designs masonry to EN 1996-1-1, and assumed what a model did not say: the units' material, group and category, the mortar's kind and strength and whether it was designed, and so the factor K, the partial factor and the strengths (open questions, 127). It checked these members against that design. The partial factor in Sweden (BFS 2024:6, Table 3:15) depends on the units' category and on whether the mortar is designed or prescribed; other national annexes set it by the class of execution.

## 5. Types

A type is the shared definition its elements follow, as a class is for its instances. Every type has:

| Member | Meaning |
|---|---|
| `id`, `name` | identity and display name |
| `category` | one of the thirty-one element categories; only elements of this category may use the type |
| `material`, `color` | the type's material text and display colour (`#rrggbb`) |
| `width`, `height` | the type's nominal size in mm, positive. The size its elements take only where the type sets it (section 5.3): a door's or window's type with a `windowFamily` or `openingFamily`, a type with a `component` (other than a contour plate's, a bolt group's or a weld's) or an `insert`, a round footing type. A door or window whose type has neither family keeps its own size |
| `layers` | optional: the compound layers (section 5.1) |
| `revision` | optional; absent means 0 |

and at most a few **payloads**, each allowed only for some categories:

| Payload | Allowed for | What it defines |
|---|---|---|
| `layers` | `slab`, `wall`, `foundation`, `roof`, `ceiling` | compound layers |
| `section` | `beam`, `column` | a member section: `rectangle`, `I`, `T`, `RHS`, `CHS`, `round`, `L`, `2L`, `U`, `sheet` or `custom`, with `width`, `depth`, `web`, `flange`, and a `custom` section's `outline` (section 5.2) |
| `framing` | `beam`, `slabEdge` | a parametric framing family (section 5.4) |
| `edgeProfile` | `slabEdge` | an edge beam's or an edge form's section (section 5.6) |
| `gutterProfile` | `gutter` (required there) | a gutter's section (section 5.6) |
| `windowFamily` | `window` | a window family; the profiles of its frame and its mullions are 0.2's (Part IV, 4.3.1, rule 15) |
| `openingFamily` | `door`, `window` | a door or window family; the opening's kind decides which. Its `material` is its frame's: `wood`, `aluminium`, `aluClad` (aluminium-clad wood) or `steel` (open questions, 89); a door's `leafMaterial`, its leaves' where it is not the frame's, is 0.2's (Part IV, 4.3.1, rule 14), and so are the profiles of its frame, its mullions and its sashes (rule 15) |
| `outline` | types with an `openingFamily` | a polygonal opening shape |
| `wallAttach` | `wall` | defaults for attached wall tops |
| `footing` | `foundation` | a round footing; the type's `width` equals its diameter |
| `component`, `voidForm`, `edgeDetail`, `insert` | `component` | a component, a void, an edge detail or a cast-in item; a `component` type MUST have one of them |
| `room`, `shaft`, `stair`, `railing`, `pipe`, `duct`, `terminal`, `furniture`, `planting`, `fire`, `road`, `parking` | their categories | the domain data of those categories |
| `generic` | `generic` (required there) | what the type stands for, its IFC class and its discipline (section 6.6) |
| `thermal` | `slab`, `wall`, `foundation`, `roof`, `ceiling`, `door`, `window` | declared thermal values (section 5.5) |
| `acoustic` | `wall`, `slab`, `door` | declared sound insulation (section 5.5) |
| `assembly`, `catalogSource`, `handbook` | any | application values (section 9.2) |
| `declaredGeometry` | any | a supplier's geometry at a declared level of detail (section 12.1) |

A type MUST NOT carry both `openingFamily` and `windowFamily`. The category-specific schemas `types/<category>.schema.json` encode the table above.

A terminal type's `terminal.kind` is one of the kinds `types/type.schema.json` lists, from air terminals and units to sanitary fixtures, appliances and radiators; `chilledBeam` is an active chilled beam. `unitHeater` is a unit heater, and the kinds of components in a pipe or a duct are section 6.9's. A terminal type's `terminal.mount` is `ceiling`, `wall`, `floor`, `roof`, `duct` for a component in a duct, or `pipe` for a component in a pipe (section 6.9).

**A component's family.** A component type's `component.family` says what its elements are: `plate` a steel plate, `bolt` a bolt group, `weld` a weld, `angleBracket` an angle bracket, `joistHanger` a joist hanger and `timberSection` a piece of a timber section; `toilet` and `basin` are sanitary fixtures of before 0.1, read in 0.1 and deprecated in 0.2, which a `terminal` now stands for (open questions, 73). Its `depth` is its third size in mm, beside the type's `width` and `height`: a plate is `width` by `depth` in its own x and y, and its thickness is its `height`. A bracket's or a hanger's sheet is `thickness` thick, a hanger's `flange` is the width of each of its nail flanges, folded outwards where it is positive and inwards where it is negative, and a punched connector's nails are in the pattern `nails`. A plate type's `holes` go through each of its plates, each centred at `x` and `y` in the plate's own frame, `diameter` across, and slotted where it gives a `length`, its length overall, along `angle` degrees from x, its ends round. Part VIII, 1 names each family's IFC class.

**A bolt.** A bolt type holds its bolt, `component.bolt`, which every bolt of its groups is: its `diameter`, in mm; its `grade`, `4.6`, `5.6`, `8.8` or `10.9`, and `8.8` where it gives none; its `kind`: `bolt`, a hex bolt with its head, where it gives none; `anchor`, an anchor bolt, a rod cast into concrete with a nut on top and an anchor plate at its foot; or `rod`, a threaded rod; its `washers`: under the head and the nut (2), under the nut (1, where it gives none) or none (0), and a rod's under both its nuts, or under the far one or its one nut; a rod's `nuts`: on both faces of the grip (2, where it gives none: through-bolted, or a base plate on levelling nuts), on the face at z = 0 only (1: into an anchor sleeve, or glued in) or none (0); and its `finish`, `hdg` hot-dip galvanized or `black`.

Reference: the type checks in `validateManifest` (`src/core/model/model.ts`), `validateOpeningFamily` (`src/core/elements/openings.ts`), `validateFootingType` (`src/core/elements/footings.ts`), `validateInsertType` (`src/core/elements/inserts.ts`).

*Part of 0.2; OpenEik 0.1 had it as the proposal `components`.* By the steward's decision of 26 October 2026 (open questions, 73), the families `toilet` and `basin` are deprecated (Part I, 9.6). A reader reads a component type of either family, and its elements, as 0.1 does. A writer SHOULD NOT write one: it writes a `terminal` instead (section 6.9), its type's `terminal.kind` `wc` for a toilet and `washbasin` for a basin, as the reference implementation turns them on load. A validator SHOULD warn about each such type (`component-retired`).

### 5.1 Layers

A layer is `{id, name, function, material, thickness, color, core}` with optional `pattern`, `inset`, `extent` and domain rules (`framing`, `joists`, `precast`).

1. `function` is one of `structure`, `substrate`, `insulation`, `finish`, `membrane`.
2. `thickness` is in mm. Only a `membrane` MAY have thickness 0; the sum of a type's layer thicknesses MUST be positive.
3. Layer ids MUST be unique within the type.
4. `core` marks the core layers. The layers before the first core layer are the exterior layers.
5. `inset` (mm, 0–3000) is allowed only on slab and roof types; `extent` (where a roof layer stops in plan) only on roof types.
6. Order: wall layers exterior first, left of the directed baseline; horizontal layers top first (Part I, 5.1).
7. When a type has layers, the element's thickness is the sum of the layer thicknesses, and the element's own `width` (walls) or `height` (horizontal elements) is ignored for its body (Part IV).
8. `thermal` (optional) is `{lambda?, air?, sd?}`: the layer's own design thermal conductivity in W/(m·K), which a calculation uses instead of its material's (a framed layer's, say, or a product's); `air`, which makes the layer an air layer and says whether it is `closed`, `slightly-ventilated` or `ventilated` (EN ISO 6946, 6.9; behind a ventilated air layer the layers outside it do not count); and `sd`, the water vapour diffusion-equivalent air layer thickness in m, from 0 to 100 000, of a membrane or foil (EN ISO 13788).

Reference: `validateManifest` (layer checks), `basicSolids` (`thickness ?? e.width`, `thickness ?? e.height`).

*Note (informative).* A wall type's layer's `framing` is a timber framing rule. What its members mean, and how a framed layer is laid out in plates, studs, headers and the rest, is said in Part IV, 7.4, which adds a horizontal rule's `battens`. A slab type's layer's `joists` is a joist rule, laid out in joists, trimmers, rim joists, headers and blocking by Part IV, 7.4.15, and a roof's `trussLayout` lays its trusses out on the walls it bears on by Part IV, 7.4.13 and 7.4.14, which add a roof opening's `header`, and frames its hip ends, as its `hips` says, by Part IV, 7.4.16.

*Note (informative).* 0.1 does not say which way a layer's fibres run. By the steward's decision of 28 October 2026 (open questions, 90), 0.2 adds a layer's `grain` and an element's main direction, which section 5.1.1 gives. By the steward's decision of 30 October 2026 (open questions, 115), 0.2 adds a layer's `drawing`, the detail family its cut is drawn with in a drawing of its type's build-up, which section 14.2 gives, and the pattern `clt` (section 4, rule 4).

### 5.1.1 A layer's grain and an element's main direction

*Part of 0.2. OpenEik 0.1 had it as the proposal `grain`: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), `grain` and `mainDirection` are members a schema of 0.1 does not know, which a reader keeps (Part I, 10). The section adds these two members and no file.*

By the steward's decision of 28 October 2026 (open questions, 90). A layer of cross-laminated timber (CLT), and any layer stiffer one way than the other, such as LVL with cross veneers, a glulam deck or a profiled sheet, has a grain: its stiffness, its strength, its deflection, its vibration and its fire resistance depend on which way its fibres run. These rules let a model say so, so that two tools that read one building read one layup.

1. **A layer's grain.** A layer of a type of the category `slab`, `wall`, `roof`, `ceiling` or `foundation` MAY carry `grain`: `"along"` where its fibres, or a sheet's strong direction, run along the element's main direction (rules 2 to 4), and `"across"` where they run square to it in the layer's plane. A layer without `grain` states none: it is isotropic, or its grain is not said. `grain` takes no other value, and no other angle.
2. **A slab's, a ceiling's or a foundation's main direction** is a direction in plan. Where the element has `panels` (section 6.3), it runs square to their `direction`, as the panels do: `(direction + 90) mod 180` degrees, counter-clockwise from the model's x axis. Where it has no `panels`, it MAY carry `mainDirection`, its main direction in degrees counter-clockwise in plan from the model's x axis. Where it carries both, `mainDirection` MUST be `(direction + 90) mod 180`, the two compared modulo 180 within 0.01°: a validator reports one that is not (`main-direction`). Where it has neither, the model states no main direction for it, and its layers' grain is not placed. On a slab with a plane (section 6.3.1), its main direction is that direction in plan projected vertically onto the plane, as a roof's `mainDirection` is (rule 4).
3. **A wall's main direction** is its `mainDirection`: `"vertical"`, up the wall, or `"horizontal"`, along its baseline, in the wall's plane; `"vertical"` where it gives none.
4. **A roof's main direction** lies in the plane of each of its faces: along the fall line of the slope-defining edge whose plane the face is (Part IV, 4.4, rule 1), square to that edge in plan, unless the roof carries `mainDirection`, degrees counter-clockwise in plan from the model's x axis, which every face takes, projected vertically onto its plane. A face of no slope lies level, and its main direction is `mainDirection`; without it, the model states none.
5. **Which elements carry it.** An element MUST NOT carry `mainDirection` unless its category is `slab`, `ceiling`, `foundation`, `roof` or `wall`: a number in degrees on the first four, and `"vertical"` or `"horizontal"` on a wall. A validator reports `mainDirection` on an element of another category (`main-direction`), and a value of the wrong kind as its schema's error.
6. **The order of the layers** is rule 6 of section 5.1: a horizontal type's top first, a wall's exterior first. So a reader knows which face each layer is on, and so where a layup is not symmetric.
7. **What it does not cover.** A layer's lamella width, its edge gluing, a product's declared values and its European Technical Assessment are product data, which applications keep in their extension members (Part I, 11.2) until a later version has a product record. A layer's strength class is its material's `strengthClass` (section 4.1), so a type whose layers are of two strength classes names two materials.
8. **Bodies and prints.** The grain and the main direction change no body: a compiler derives the same bodies with them or without them (Part IV), and the IFC4 print of Part VIII, 1 is the same until a later version says how IFC carries them.

A CLT floor type of five lamellas 40 mm thick, C24 along and C14 across, top first, has the layers `{"function": "structure", "material": "CLT C24", "thickness": 40, "grain": "along"}`, then `"CLT C14"` `"across"`, `"CLT C24"` `"along"`, `"CLT C14"` `"across"` and `"CLT C24"` `"along"`, each with its id, name, colour and `core`. A floor of it whose panels, 2.4 m wide, lie side by side along y and span along x has `"panels": {"width": 2400, "direction": 90}`: its main direction is 0°, along x, and so are the fibres of its outer layers. A layup of seven layers with two along at each face, which no rule of alternation gives, is along, along, across, along, across, along, along. A wall over a wide opening whose outer layers run horizontally, so that it spans the opening as a beam does, has `"mainDirection": "horizontal"`.

*Note (informative).* The reference implementation offers CLT walls and floors in its library as one core layer each, and does not write `grain` or `mainDirection` yet: the layup and the span direction are left to the project, which these rules let a model state.

### 5.2 Sections

A `section` gives a member's cross-section: its `shape`, one of `rectangle`, `I`, `T`, `RHS`, `CHS`, `round`, `L`, `2L`, `U`, `sheet` and `custom`, and its sizes. For a beam, `width` runs across the member and `depth` up; for a column, `width` runs along x and `depth` along y before the column's `turn`, the section's top towards +y. Except for a `T`, a `2L`, a `round` and a `custom` section, which have only their own rules (rules 1 to 3, and 8 to 10), `web < width` and `2 × flange < depth` MUST hold; an `L` section needs `web < width` and `flange < depth`; a `sheet` section's `width` and `depth` follow its profiled sheet (cover width and profile height).

1. A `T` section is a flange on top of a web, which stands under the middle of the flange. `width` is the flange's width, `depth` the overall depth, `web` the web's thickness and `flange` the flange's. `web < width` and `flange < depth` MUST hold.
2. A `2L` section is two identical angles back to back: their vertical legs stand `gap` apart, and their horizontal legs point outward at the top. `width` is the overall width, both horizontal legs and the gap; `depth` is a vertical leg's length, `web` a vertical leg's thickness and `flange` a horizontal leg's. Each horizontal leg is `(width − gap) / 2` long. `gap < width`, `web < (width − gap) / 2` and `flange < depth` MUST hold. The angles' legs may be equal or unequal: a horizontal leg, `(width − gap) / 2`, need not be as long as a vertical one, `depth`.
3. A `round` section is a solid round bar: `width` and `depth` are both its diameter, and MUST be equal, within 10⁻⁶ mm. Its `web` and `flange` are not read, and no rule applies to them.
4. `gap` is the distance between the backs of a `2L`'s angles, in mm, from 0 to 10 000; absent, it is 0. A section of any other shape MUST NOT carry it.
5. On a `T`, `radius` is the radius of its two root fillets, one each side of its web under its flange; on a `2L`, that of each angle's root fillet, where its vertical leg meets the underside of its horizontal leg. On an `RHS` it is the radius of its four outer corners, and its inner corners are rounded to `radius` less the thicker of its `web` and `flange`, square where that is not more than 0; without a `radius`, or with 0, all eight corners are square. On a `rectangle` it rounds the four corners (rule 7). It is not read for a `round` or a `custom` section.
6. `designation` names the section as its product standard does, such as `IPE 200`, `HEA 160` or `L 100 × 100 × 10`, and `grade` the grade of the members' material, such as `S355J2`, `GL30c` or `C24`: plain strings (section 8, rule 3). A type can state a grade twice, in its section's `grade` and in the `strengthClass` of the material its `material` names (section 4.1). Where the section's `grade` is not empty, it is the members' grade: a reader that reads a member's grade MUST take it, and where the two differ a validator MAY warn (decided on 22 October 2026; open questions, 42).
7. A `rectangle` with a `radius` is a solid rectangle with its four corners rounded: each corner a quarter circle of that radius, tangent to the two sides that meet there, as a timber section with rounded corners is, such as wane-edged timber hewn flat on its faces so that the courses of a wall bear flat on each other. Without a `radius`, or with 0, its corners are square. `radius` MUST NOT be more than half the smaller of `width` and `depth`, within 10⁻⁶ mm: at half, its two shorter sides are half circles, and a section as wide as it is deep is a circle. Its area is `width × depth − (4 − π) × radius²` and its centroid its middle; its area, its second moments of area and its members' volume and weight are the rounded shape's, its arcs exact. A command that writes a larger `radius` is refused (Part V). A reader that reads a model holding one, without the whole-model check (Part III, 12), takes the section's corners as rounded to half its smaller side, and so does a compiler or a bridge that meets one.
8. A `custom` section is the region its `outline` gives: `outline.points`, the contour of its outside, less each of `outline.voids`, each `{points}`, the contour of a hole through it. A `custom` section MUST carry `outline`, and a section of any other shape MUST NOT. A contour is a list of entries in mm in the section's own plane, `x` running as the section's `width` does and `y` as its `depth` does: for a beam, `x` across the member towards its left, seen from its start, and `y` up, before its roll; for a column, `x` and `y` before its `turn`. A contour is closed by its last side, from its last entry back to its first, and does not repeat its first point; it may run either way round. Each entry is one of:
   - `[x, y]`, a corner;
   - `[x, y, r]`, a corner rounded by an arc of radius `r` mm, tangent to the two straight sides that meet there, each of which it takes `r × tan(θ / 2)` off, `θ` being the angle through which the contour turns at the corner; with `r` 0, the corner is square;
   - `{"through": [x, y]}`, a point that the side from the entry before it to the entry after it passes through: that side is the arc of the circle through the three points.

   A wane-edged log 200 mm wide and 250 mm deep, its corners rounded to 50 mm, is `{"points": [[0, 0, 50], [200, 0, 50], [200, 250, 50], [0, 250, 50]]}`, the section that a `rectangle` with a `radius` of 50 is too (rule 7); a D-log 150 mm wide and 200 mm deep, flat on its inner face, its top and its underside and round on its outer face, is `{"points": [[0, 0], [150, 0, 100], [150, 200, 100], [0, 200]]}`; glulam 90 mm × 315 mm with its edges chamfered 10 mm, `{"points": [[10, 0], [80, 0], [90, 10], [90, 305], [80, 315], [10, 315], [0, 305], [0, 10]]}`; and a round log 300 mm across, hewn flat and 240 mm wide on its underside, `{"points": [[-120, 0], [120, 0], {"through": [0, 240]}]}`, 300 mm wide and 240 mm deep. A void is written as the outline is: a bored core 40 mm across in the middle of the wane-edged log is `{"points": [[80, 125], {"through": [100, 105]}, [120, 125], {"through": [100, 145]}]}`.
9. A `custom` section's contours MUST each hold three entries or more, at least two of them corners, and no two `through` entries one after the other (the last entry and the first are one after the other). Within a contour:
   - no entry lies less than 0.1 mm from the entry before it, the first from the last, and no `through` entry lies within 0.01 mm of the straight line between the corners beside it;
   - a corner has a radius only where the contour turns, and both its sides are straight: neither the entry before it nor the one after it is a `through`;
   - each straight side is long enough for the corners at its ends: what their radii take off it, together, is not more than its length, within 0.01 mm;
   - no side crosses or touches another, the arcs of rounded corners and of `through` entries among them: two sides that follow one another share their one point and meet nowhere else.

   Each void MUST lie inside the outline, with no side of it crossing or touching a side of the outline or of another void, and no void inside another. A `custom` section's `width` and `depth` MUST be the width and the height of its outline's bounding box, its arcs included, within 0.1 mm. Its `web`, `flange` and `radius` are not read, and no other rule of this section applies to them.
10. A `custom` section is placed by the middle of its outline's bounding box, as every section is placed by the middle of its `width` and `depth` (Part IV, 6.5): a point `[px, py]` of its outline lies `px − mx` across a beam and `py − my + depth / 2` up from its underside (Part IV, 6.2, rule 3), and `(px − mx, py − my)` from a column's centre before its `turn` (Part IV, 6.1), `(mx, my)` being that middle. Its area, its centroid and its second moments of area are those of its region, its arcs exact and its voids taken out; its members' volume is that area along their length, and their weight that volume by their material's density (section 4.1). Plans and sections cut it as its region, its voids open, with its type's material and hatch, as they cut any section. A compiler MAY draw an arc as straight pieces: the arc is the rule, and the pieces are the compiler's, as a pipe's circle is (Part IV, 12.3, rule 5). Part VIII, 1.4 says how a bridge writes a `custom` section and a `rectangle` with a `radius`.

*Note (informative).* The sizes of an `I`, `T`, `L`, `2L` or `U` section describe flanges and legs of even thickness with square toes, rounded only at the root (`radius`). A rolled section can have more: toe radii on an angle, a tee or a channel, and sloping flanges on a UPN channel or an IPN beam. 0.1 has no members for them, and the body Part IV derives, with the axis it sets (Part IV, 6.5), is the one the sizes describe. A reader that needs a rolled section's own properties, its area or its second moments of area say, can take them from its product standard by its `designation`: EN 10056-1 gives an `L 100 × 100 × 10` a root radius of 12 mm and toe radii of 6 mm.

*Note (informative).* Rules 7 to 10, and what rule 5 says of an `RHS` and a `rectangle`, were decided by the steward on 27 October 2026 (open questions, 87). An outline's entries are those an author can write by hand without working out a point the section does not show: a rounded corner by its radius, where its arc's ends would need its tangent lengths, and an arc that meets its sides at an angle, as a hewn log's does, by a point it passes through, where its centre and its radius would need working out. Both are IFC4's own: each arc, a rounded corner's and a `through` entry's, is an IfcArcIndex of three points, so a bridge writes an outline with nothing lost (Part VIII, 1.4). A `rectangle`'s `radius` was taken and not read until then, so a model may hold one larger than rule 7 allows. The reference implementation reads a `rectangle`'s `radius` as rule 7 says, since 27 October 2026 (main `d12a2c54`): it draws the rounded corners in 3D, plans and sections, takes the section's area and second moments from the rounded shape (`width × depth − (4 − π) × radius²`), and writes an IfcRoundedRectangleProfileDef. It refuses a larger `radius` only in a command that writes it, never in a replay, and leaves alone a type that a project holds already with that radius, so that such a project opens as it did, its log replays and its other commands are taken, where Part V, 6, rule 3 refuses a command that leaves a model invalid; it draws and measures such a section with its corners rounded to half its smaller side (open questions, section 2, row 38). It reads a `custom` section as rules 8 to 10 say since 28 October 2026 (main `35bef7f5`): it draws the outline in 3D, plans and sections, takes the section's area, centroid and second moments from its region exactly, and writes an IfcArbitraryClosedProfileDef, or an IfcArbitraryProfileDefWithVoids. It refuses an outline that breaks rule 9 in a command that writes it. On an `RHS`, it draws rounded corners only where the section's `web` and `flange` are equal and its `radius` is at least its wall and less than half its smaller side, and refuses a type with another `radius` (`steelCells`); this section asks neither.

Reference: `validateMemberSection` (`src/core/elements/sectionRules.ts`), which `validateManifest` calls for every type; `partGrade` (`src/core/pieces/pieces.ts`), which takes a section's `grade` before its type's material (rule 6); `steelCells` (`src/core/geometry/steelProfiles.ts`), an `RHS`'s corners (rule 5). On the branch named above: `rectangleRadius`, `roundedRectangleArea` and `roundedRectangleInertia` (`src/core/geometry/roundedRectangle.ts`) and `refuseRectangleRadius` (`src/core/elements/sectionRules.ts`), rule 7.

### 5.3 Sizes a type sets

A door's or window's type with a `windowFamily` or `openingFamily`, a component type with a `component` (other than for a contour plate, a bolt group or a weld, which keep their own sizes), a round footing type and a cast-in item's type set their elements' `width` and `height`.

A beam whose type has a `section` takes its depth from the section: its `height` MUST equal `section.depth`.

No other type sets an element's `width` or `height`. A door or a window whose type has neither a `windowFamily` nor an `openingFamily` has its own `width` and `height`, whatever its type's are, and so has an element whose type is a void form or an edge detail (`voidForm`, `edgeDetail`).

1. A writer MUST write such an element's `width` and `height` equal to its type's (for a sectioned beam, its `height` equal to the section's depth).
2. A command that sets a different `width` or `height` on such an element MUST be refused (Part V). An element file that says so fails the whole-model check (Part III, 12, rule 5): a validator MUST report it as an error, and a reader that performs the check refuses the folder (Part III, 14, rule 3).

Reference: `typeSizes` and `typeSetSizes` in `src/core/model/typeSets.ts`; `refuseTypeSetSizes` in `run`; `typeSetInFiles` in `src/core/versions/eikFolder.ts`.

*Note (informative).* The rule for sectioned beams was decided on 11 October 2026. The reference implementation meets it since Stomra pull request #777 (live since 11 October 2026): it sets a sectioned beam's `height` to its section's depth on every command and on load, and refuses a patch that disagrees (Part IV, 6.3).

### 5.4 Framing families and their expression language

A `framing` payload is a parametric 2D family, swept across its plane: `{name, previewLength, parameters, parts}` with optional `trussStyle`, `edgeAnchor` and `tail`. Its numbers are **expressions**: strings in the language below. This language is normative: every implementation that derives framing bodies MUST evaluate it exactly as stated.

**Grammar** (ISO 14977 EBNF; white space between tokens is ignored):

```ebnf
expression = sum ;
sum        = product , { ( "+" | "-" ) , product } ;
product    = unary , { ( "*" | "/" ) , unary } ;
unary      = ( "+" | "-" ) , unary | primary ;
primary    = number | call | name | "(" , sum , ")" ;
call       = function , "(" , sum , { "," , sum } , ")" ;
function   = "sqrt" | "abs" | "min" | "max" | "sin" | "cos" | "tan" | "atan" ;
number     = digit , { digit } , [ "." , { digit } ] | "." , digit , { digit } ;
name       = letter , { letter | digit | "_" } ;
letter     = "A" | … | "Z" | "a" | … | "z" ;
digit      = "0" | … | "9" ;
```

**Tokens.** A source is split into numbers, names and the characters `( ) + - * / ,`. Any other character except white space makes the expression invalid. An expression is 1 to 240 characters and at most 160 tokens.

**Names.** A name not followed by `(` is a value from the scope:

| Name | Value |
|---|---|
| `Length` | the member's length in mm. For a beam: the length of its set-out line (Part IV, 6.2), `hypot(plan length, endRise)` |
| a family parameter | the element's `framingParameters[name]` if the parameter's scope is `instance` and the element gives it, else the parameter's `value` |
| `Overhang`, `TipTop` | in a tail cut only: that tail's overhang, and the height of the tail tip's top over the bearing plane |

A name followed by `(` MUST be one of the eight functions. An unknown name or function makes the expression invalid.

**Functions.** Angles are in degrees.

| Function | Arguments | Value |
|---|---|---|
| `sqrt(x)` | 1 | √x; invalid when x < 0 |
| `abs(x)` | 1 | \|x\| |
| `min(a, b, …)`, `max(a, b, …)` | 2 to 8 | the least or greatest argument |
| `sin(x)`, `cos(x)` | 1 | of x degrees |
| `tan(x)` | 1 | of x degrees; invalid when \|cos x\| < 10⁻⁹ |
| `atan(x)` | 1 | the angle in degrees whose tangent is x |

**Evaluation.** `+ - * /` are IEEE 754 double operations, left to right within a precedence level; unary signs bind tighter than `*` and `/`. The expression is invalid when its value is not finite or its magnitude exceeds 1 000 000.

**Parameters.** A family has at most 32 parameters `{name, value, scope, min, max}`. A parameter's name matches `^[A-Za-z][A-Za-z0-9_]*$`, is unique, is not `Length` and is not a function name. `min ≤ max`, and the value used (the type's, or the element's instance override) MUST lie in `[min, max]`. An element's `framingParameters` MAY name only parameters whose scope is `instance`.

**The family plane** of a beam: `x` runs along the member's set-out line from its start, `z` at right angles to it, upwards, from that line (the element's start point at `level + offset`, rising by `endRise` over the span; Part IV, 6.2). A family point `(x, z)` lies at plan position `start + u (x cos α − z sin α)` and elevation `level + offset + z cos α + x sin α`, where `u` is the unit plan direction and `α` the line's slope. Each part is swept across the plane by its `width`, centred on the plane.

**Parts.** A `member` part is a straight member from point `a` to `b` with a `width` and a `depth`; a `profile` part is a polygon `outline` with a `width`; a `plate` part is a convex nail plate on the members it lies `on`. `when` makes a part exist only while its value is greater than 0. `cuts` and `cutBy` cut a part by lines or by other parts. [Part IV, 6.6](https://openeik.org/spec/0.4/compiler#6.6), of 0.2, says exactly how: where each part lies and how it is cut, a tail's cuts and a slab edge element's plane, and what a family that cannot be built is.

Reference: `framingSchema`, `framingExpression` and `framingProfiles` in `src/core/elements/framing.ts`; the placement in `src/core/geometry/framingGeometry.ts`.

Example, from a rafter family in the reference implementation's examples: a `profile` part of `width: "Width"` whose outline runs from `["-Overhang", "-Rise*Overhang/Length"]` through `["0", "0"]`, `["0", "Notch"]`, `["Seat", "Notch"]` and `["Length", "Rise"]` to `["Length", "Rise+Depth"]`, with parameters `Width` and `Depth` of scope `type` and `Rise`, `Overhang`, `Seat` and `Notch` of scope `instance`.

### 5.5 Building physics on types and rooms

A type MAY carry the values its elements have as a whole, declared by a manufacturer or a test, which a calculation uses instead of deriving them:

| Member | On | Meaning |
|---|---|---|
| `thermal.u` | the layered categories, `door`, `window` | the thermal transmittance U in W/(m²·K), greater than 0, at most 20: of a door or a window as a whole, its frame included (EN ISO 10077); of a layered type tested as a whole, such as a prefabricated element, whose U replaces the one its layers give (EN ISO 6946) |
| `thermal.g` | `door`, `window` | the total solar energy transmittance of the glazing, from 0 to 1 (EN 410) |
| `thermal.frameFraction` | `door`, `window` | the share of the opening's area that is frame, from 0 to less than 1 |
| `thermal.lightTransmittance` | `door`, `window` | the glazing's visible light transmittance τv, from 0 to 1 (EN 410) |
| `acoustic.rw` | `wall`, `slab`, `door` | the weighted sound reduction index Rw in dB, from 0 to 120 (EN ISO 717-1) |
| `acoustic.lnw` | `slab` | the weighted normalized impact sound pressure level Ln,w in dB, from 0 to 120 (EN ISO 717-2) |

1. A type of a layered category carries `thermal.u` only, and a wall's or a door's `acoustic` carries `rw` only.
2. A door's or a window's type SHOULD carry `thermal.u`; one with glazing SHOULD carry `thermal.g` too.
3. An element's fire resistance class stays the fire profile's `fireClass` (section 8), and a material's reaction to fire is its `fireReaction` (section 4.1).
4. A roof window has the values of the `window` type it names (section 6.3, `roof.windows`).

A room type's domain data, `room` (section 5), MAY also say:

1. `heated`: whether its rooms are heated, true for above 10 °C, as a heated floor area such as BBR's Atemp counts them;
2. `temperature`: the rooms' design indoor temperature in °C, from −50 to 60;
3. `use`: what the rooms are used for, a plain string of at most 80 characters (section 8, rule 3), such as `residential`, `office` or a code of a national list of uses.

### 5.6 Slab edge and gutter sections

By the steward's decision of 26 October 2026 (open questions, 74 and 75), so that Part IV, 4.6 and 4.7 derive these elements' bodies from the records alone.

A slab edge type's `edgeProfile` gives its section: its `shape`, a `rectangle`, an `L` or a `U`; its `width` and `depth`; an `L`'s or a `U`'s `web`, the thickness of its upstands, and `flange`, the thickness of its bottom leg (a `rectangle` carries both, and they mean nothing); its `anchor`, `slabTop` or `structureBottom`, the height its section hangs from; and, where it has them, a `facing` (a board of a `material` and a `thickness` on its outer face, as deep as the section or its own `depth`), a `beam` (a `width` and a `depth` to which it thickens its host under its edge) and a `chamfer` (a `width` and a `height` cut from its upstand's top inner corner). A gutter type's `gutterProfile` gives its section: a `halfRound` of an inner `diameter`, or a `box` of an inner `width` and `depth`, both `thickness` thick; and its `horizontalOffset` and `verticalOffset`, which its elements' own add to (section 6.3).

1. An `L`'s or a `U`'s `flange` MUST be less than its `depth`, and its `web` (a `U`'s two webs together) less than its `width`, so that the section has an inside.
2. A `chamfer` MUST be on an `L` or a `U`, its `width` at most the `web` and its `height` at most the `depth` less the `flange`: it is cut from the upstand, above the bottom leg.
3. A `halfRound`'s `gutterProfile` MUST give its `diameter`, and a `box`'s its `width` and `depth`.

Reference: `chamferProblem` in `src/core/elements/edgeChamfer.ts` and the type checks in `validateManifest` (`src/core/model/model.ts`: "Profile web and flange must leave an open interior", "A half-round gutter needs a positive diameter", "A box gutter needs a positive width and depth").

## 6. Elements

An element is one placed thing of the building: a wall, a slab, a door. Every element has:

| Member | Meaning |
|---|---|
| `id`, `name` | identity and display name (non-empty) |
| `category` | one of the thirty-one categories (section 6.2) |
| `typeId` | its type, of the same category |
| `levelId`, `offset` | its level and the offset from it (Part I, section 4) |
| `height`, `width` | positive, at most 100 000 mm; their meaning depends on the category |
| `profile` | 1 to 500 plan points; its meaning depends on the category |
| `revision` | Part I, section 8 |

Everything else is optional. The members of section 6.3 belong to the categories it lists; the others that the element schema names, `declaredGeometry` (section 12.1) among them, may be on any element, though most mean something for a few categories only (section 6.3 describes some).

An element or a type imported from IFC may carry `ifcSource`, `{globalId, entity, units?, properties?}`: the GlobalId and the IFC class it was imported from, the units of its numbers, and the property sets it was imported with, `{<set name>: {<property name>: value}}`, each value a text, a number or a truth value. `units` has one value, `"specification"`: every number of `properties` is in this specification's units, a length in millimetres and an angle in degrees (Part I, 3), any other measure in its SI unit. A writer that imports a file SHOULD convert its numbers so and say so with `units`. Where `units` is absent, the record does not say which units its numbers are in, as records written before 29 October 2026 do not, and a reader does not convert them. A bridge keeps the GlobalId (Part VIII, 1, rule 3) and prints the property sets back, typed by IFC4's templates where IFC4 defines the set, a number of a measure with a unit only where `units` says which units it is in, and under a name that does not start with `Pset_` where it does not (Part VIII, 1, rule 5, and 1.5). Reading IFC (Part VIII, 2) adds the predefined type and the user-defined type an object was read with (`ifcSource.predefinedType`, `objectType`), and `shape: "given"` on an element whose shape is the body its reader gave rather than its members.

### 6.1 Element rules every reader needs

1. An element's `typeId` MUST name a type of the same category, and its `levelId` a level.
2. A wall's and a beam's profile MUST be exactly two points at least 10 mm apart in plan.
3. A slab's, ceiling's, roof's or shaft's profile MUST be an outline of three points or more, with an area of at least 100 mm², no two consecutive points less than 0.1 mm apart, and no edge crossing or touching another. Two edges that follow one another share a point, and meet there without touching in this rule's sense; they MUST NOT meet anywhere else, as they do where an outline turns back along itself. A foundation's profile is such an outline, or, for a round footing type, one point: the centre.
4. A room's profile is one point (its boundary is found from the walls) or an outline of three points or more drawn by hand; never two points. A room MUST carry `room` with its `number`, unique on its level. Its boundary found from the walls, its ceiling and its quantities are 0.2's, in Part IV, 7.5.
5. A door or window MUST be hosted by a wall (`hostId`). It MUST fit its host: `along ≥ 0`, `along + width ≤` the host's length, `offset ≥ 0`, and its top `offset + height` at most the host's height (or under the host's attached top where that rises higher). Where its top is over the host's height, each corner of its rectangle, or each point of its type's outline placed at the opening (Part IV, 4.3), MUST lie at or under the host's attached top at that point's distance along the host's centreline, the top that Part IV, 5.3, rules 4 and 5 derive. Two openings in one wall MUST NOT overlap. Two openings overlap when their shapes in the wall's elevation share an area: their rectangles, from `along` to `along + width` and from `offset` to `offset + height`, or, where a type gives an outline, that outline placed at its opening. Openings that only touch, along an edge or at a corner, do not overlap.
6. A slab edge element MUST be hosted (`hostId`) by a slab or a foundation. A rebar element MUST be hosted by a slab, a foundation, a beam, a column, a slab edge element or a wall (section 6.1.1), and a mesh element by a slab, a foundation or a wall. A rebar element carries `reinforcement` or `bars`; a mesh element `reinforcement` or `sheet`. The rules that place reinforcement hold in its host's own frame, in a beam, a column or a slab edge element as in a slab. Where they name the host's thickness, a beam's, a column's or a slab edge element's is its section's depth across the plane the bars lie in. A slab edge element's `hostEdgeId` MUST name an edge of its host's outline (rule 10), the edge it runs along (Part IV, 4.6); a round footing has none. Where slab edge elements thicken their host (Part IV, 4.6, rule 9), the thickened edges MUST fit it, as Part IV, 4.6, rule 11 says.
7. A stair's profile is two points (its start and a point up its first run, or for a circular stair its centre and its start), and it carries `stair`. Its numbers and the layout of a straight, L or U stair are 0.2's, in Part IV, 7.3.
8. A component's profile is two distinct points (its insertion point and orientation) unless it is a cast-in item placed by `insertAt`.
9. A room separator's and a property line's profile is two points or more.
10. The outline's boundary edge ids (`edgeIds`) of a slab, foundation, ceiling, roof or shaft, when present, MUST be unique and one per profile point.
11. A railing MUST meet the host and edge rules of section 12.2: it stands on a stair, on the outline of a slab or a shaft, or on a line of its own.
12. A generic element's profile is one point, or two distinct points; it has no `hostId`, and its shape is declared geometry, its own or its type's (section 6.6).
13. A gutter MUST be hosted (`hostId`) by a roof, and its `hostEdgeId` MUST name an edge of that roof's outline (rule 10), the edge it hangs on (Part IV, 4.7). Its `profile` is not used for placement.

The category schemas `elements/<category>.schema.json` encode the rules a schema can state (point counts, required members, members that do not belong to the category). The rest a validator checks in code.

Reference: `validateElement` in `src/core/model/model.ts` and the validators it calls; for rule 6's bar sets, `validateBars` and `barHosts` in `src/core/elements/bars.ts`.

*Note (informative).* Rule 6's hosts of a rebar element were decided by the steward on 26 October 2026 (open questions, 81): an edge beam's bars, and a beam's or a column's cage, name the member they lie in. A host's own frame is a beam's set-out line, across it and up from its underside (Part IV, 6.2), a column's base and its section's x and y (Part IV, 6.1), and a slab edge element's section along its host's edge (Part IV, 4.6). A bar set's `origin`, `xAxis`, `yAxis` and `along` are in the model's axes whatever its host, as in a slab; its `face`, `bottom` or `top`, says which of its host's faces it lies at in that frame. How bars bend, and where reinforcement in a layer lies, are reference behaviour (Part IV, 7.2). The reference implementation lays a bar set in a slab, a foundation, a beam, a column or a wall, and reinforcement in a layer only in a slab or a foundation (open questions, section 2, row 37).

### 6.1.1 A rebar element in a wall

*Part of 0.3. OpenEik 0.2 had it as the proposal `wallRebar` (open questions, 122): a model of 0.2, read by 0.2's rules (Part I, 9.6, rule 3), is read by it only where a caller asks for it (decision D12).*

A wall hosts a rebar element as it hosts a mesh element: a cast wall's vertical and horizontal sets, its bars round an opening and its starter bars name the wall they lie in. In a model of 0.2 that does not ask for the proposal, a rebar element hosted by a wall is reported (`host-category`), as section 6.1, rule 6 says.

1. **A wall hosts a rebar element.** A rebar element MAY be hosted (`hostId`) by a wall, beside the hosts section 6.1, rule 6 names. It carries `reinforcement` or `bars`, as in every host.
2. **A wall's own frame.** The rules that place reinforcement hold in a wall's own frame, as they do in a slab's: its directed baseline (Part I, 5.1) from its start at its base, `z0 = elev(levelId) + offset` (Part IV, 4.1); `x` along the baseline, `y` across it to its left, the side of its first layer and its exterior (Part I, 5.1, rule 2), and `z` up. A bar set's `origin`, `xAxis`, `yAxis` and `along` are in the model's axes, as in every host.
3. **Its thickness.** Where the rules that place reinforcement name the host's thickness, a wall's is its thickness: its type's layers' total, `T`, or its `width` where its type has no layers (Part IV, 4.1). Its concrete is the layers its type marks `structure`, or the whole wall where it marks none.
4. **The frame a set was written in.** A bar set in a wall MAY give `anchor`, `{at, dir}`: the wall's frame its placement was written in, `at` the wall's start at its base and `dir` the unit direction of its baseline in plan. A writer that moves or turns the wall SHOULD carry such a set with it, turned about the vertical and moved from the anchor's frame to the wall's new one, and write the new frame as its `anchor`. A reader reads the placement as it is written.
5. **Its print.** A bridge prints such a set as it prints one in its other hosts (Part VIII, 1): an IfcReinforcingBar.

Reference: `barHosts`, `barAnchor`, `followedBars` and `wallBars` (the set `wall` of `rebar.quick`) in `src/core/elements/bars.ts`. In `@openeik/tools`: `hostsOf` in `packages/openeik/src/check.js`, for a model of 0.3, and for one of 0.2 asked for with `proposals: ["wallRebar"]`.

*Note (informative).* The reference implementation lays a bar set in a wall and writes one (section 2 of the open questions, row 37), which 0.2's rule 6 does not allow: the steward decided on 31 October 2026 that a wall hosts a rebar element in 0.3 (open questions, 122), and this section was written as a proposal for 0.3, which the cut of 0.3 made a rule until the cut of 0.3 makes it a rule.

### 6.2 The categories

| Category | Discipline folder | Profile | `height`, `width` | Notes |
|---|---|---|---|---|
| `wall` | architecture | centreline, 2 points | height above the base; thickness from the type's layers, else `width` | top: section 6.4 |
| `slab` | architecture | outline | thickness from layers, else `height` | top face at level + offset |
| `foundation` | structure | outline, or 1 point (round footing) | as slab | |
| `ceiling` | architecture | outline | as slab | |
| `roof` | architecture | outline | as slab; pitched by `roof` | Part IV, 4.4 |
| `beam` | structure | plan axis, 2 points | depth and width without a section; the section's otherwise | underside at the start |
| `column` | structure | centre, first point | length; side of a square column without a section | base at level + offset |
| `door`, `window` | architecture | not used for placement | the opening's size | hosted by a wall |
| `slabEdge` | structure | not used for placement | the edge beam's size | hosted on a slab edge |
| `rebar`, `mesh` | structure | as their payload says | bar or sheet sizes | hosted |
| `component` | architecture or structure (Part III) | insertion point and orientation | from the type | plates, brackets, bolts, welds, cast-in items |
| `gutter` | architecture | not used for placement | from the type | hosted on a roof edge (`hostId`, `hostEdgeId`) |
| `room`, `roomSeparator` | architecture | point or outline; line | room height | no body |
| `shaft` | architecture | outline | height to its top | opens the slabs, ceilings and roofs it passes, and a stair's shaft foundations too (Part IV, 4.2, rule 8); no body |
| `stair`, `railing` | architecture | start and direction; line or host edges | from type and payload | derived runs and balusters |
| `pipe`, `duct`, `terminal` | systems | run of points; a terminal's point, and the way it faces (section 6.9) | sizes from type and payload | a run's body: Part IV, 12; a component in a pipe or a duct is hosted on it (section 6.9) |
| `terrain`, `grading`, `propertyLine` | site | points, outline, boundary | | surfaces; no body |
| `furniture`, `planting`, `road`, `parking`, `fire` | architecture | point or line | from the type | |
| `generic` | architecture, or the one its `generic.discipline` names | insertion point, or insertion point and orientation | not used: its shape is its declared geometry | a thing no other category fits; never hosted (section 6.6) |

A category that is not in this table is not one of 0.1's: an element or a type of such a category, one a later version adds, is kept as Part I, 10, rule 5 says. 0.2 adds `member`, a member between two points, which section 6.2.1 gives.

1. A category MUST be a plain name: a lower-case ASCII letter, then ASCII letters and digits, at most 32 characters in all (`^[a-z][A-Za-z0-9]*$`), and no device name of a common operating system, `con`, `prn`, `aux`, `nul`, `com0` to `com9` or `lpt0` to `lpt9`, in any case. The thirty-one are such names, in lower camel case (`slabEdge`), and so is every category a later version adds. `elements/element.schema.json` and `types/type.schema.json` state the rule, so a record whose category breaks it does not validate (Part III, 3, rule 2).

A category names a folder of a record's path (Part III, 4.2), and is not escaped (Part III, 2): rule 1 makes it a name that every common file system takes as it is written, as Part III, 2 makes a name of an id.

*Note (informative).* Rule 1 was decided by the steward on 15 October 2026 (open questions, 24). The longest of the thirty-one categories, `roomSeparator`, has 13 characters; 32, the most a registered prefix has (Part I, 11.1), leaves room for the categories of later versions. The device names are those of Part III, 2, rule 5 that a plain name can spell. The reference implementation's categories are an enumeration of plain names.

### 6.2.1 A member between two points

*Part of 0.2. OpenEik 0.1 had it as the proposal `member`: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), an element or a type of the category `member` is of a category 0.1 does not have, which a reader keeps (Part I, 10, rule 5). The section adds the category `member` and one member of its elements, `end`, and no file: its elements and types are records of Part III, 4's layout.*

By the steward's decision of 30 October 2026 (open questions, 110), asked by an analysis application: one rule for linear members in any direction, and a category for what is neither a beam nor a column. A beam is set out on its underside and needs a run in plan (section 6.1, rule 2; Part IV, 6.2), and a column stands plumb on its base (Part IV, 6.1), so neither records a brace, a strut or a tie that runs steeply, or plumb, from one point to another. A member's record gives the two ends of the line through the middle of its section, and Part IV, 6.7 builds it as Part IV, 6 builds a beam.

1. **The category.** `member` is a linear structural member that is neither a beam nor a column: a brace, a strut, a tie, a member of a truss or of a frame, a raking post. Its discipline folder is `structure` (Part III, 4.1), and in the owners' layout it is structure's (Part III, 4.4, rule 2). Its type is of the category `member`.
2. **Its type** MAY carry a `section` (section 5.2), as a beam's type does: its `width` runs across the member and its `depth` square to it, before the member's roll (Part IV, 6.7). It carries no other payload of section 5's table but those any type may carry; its schema sets each to `false`.
3. **Its line.** Its profile is two points in plan, its start and its end, which MAY be one point, so that the member stands plumb. Its start is its profile's first point at `elev(levelId) + offset`, and its end its second point at `elev(end.levelId) + end.offset`: it MUST carry `end`, `{levelId, offset}`, whose `levelId` MUST name a level of the model (Part I, 7). The two points lie on the member's reference line, the line through the middle of its section (Part IV, 6.5), and MUST be at least 10 mm apart in space (`member-length`).
4. **Plumb.** A member is plumb where its two plan points lie within 10⁻⁶ mm of each other. A writer SHOULD write a plumb member's two plan points equal, and a validator SHOULD warn of a member whose plan points lie less than 1 mm apart but not within 10⁻⁶ mm (`member-plumb`): its section turns with the direction from one to the other (Part IV, 6.7, rule 2), which so short a run in plan hardly sets.
5. **Its size.** With a section, the section sizes the member (Part IV, 6.3): its `height` MUST equal `section.depth`, as a sectioned beam's does (section 5.3), and its `width` is not read. Without a section, its section is a rectangle `width` across and `height` deep.
6. **Its members.** It MAY carry `roll`, `taper` and `joins`, which mean for it what they mean for a beam (section 6.3; Part IV, 6.4 and 6.7); a taper needs a rectangular section, as a beam's does. It MUST NOT carry `endRise`, `turn`, `base`, `connection` or another member that section 6.3 gives other categories: its schema sets each to `false`.
7. **What its record does not say.** A member's record says where it is and what its section is. How its ends are supported, released or connected in an analysis is no member of it: an application keeps that in its own records, or in extension members of its own (Part I, 11.2), which a member carries as a beam does. Its `joins` say only where its body is cut (Part IV, 6.4).
8. **One element for one piece.** A beam and a column keep their records and their meaning: a beam with an `endRise` and a column are read as in 0.1, and Part IV, 6.7, rule 9 reads either's reference line as a member's line. A writer writes each linear piece as one element, a beam, a column or a member, and never as two. Which is its choice: a beam is set out by its underside, a column stands on its base, and a member is set out by the middle of its section, in any direction.
9. **Commands.** A command processor makes and changes a member and its type as any element and type (Part V, 1, rule 5), holding each to this section's schemas. It refuses an `end` whose `levelId` names no level (rule 3), and a `height` other than its type's section's depth (rule 5), as it refuses a top constraint's level and a beam's height. A member's `end` on a level reaches to it as a top constraint does: `level.delete` is refused while one does, unless `moveTo` names the level they move to, and the end moves to it keeping its height (Part V, 5, rule 4).

A brace of a square hollow section 100 × 100 × 5 from the foot of a column at [0, 0] on level 0, elevation 0, up to [6000, 0] 150 mm under level 1, elevation 3600, is `{"category": "member", "profile": [[0, 0], [6000, 0]], "levelId": "level-0", "offset": 0, "end": {"levelId": "level-1", "offset": -150}, "width": 100, "height": 100, …}`, its type `{"category": "member", "section": {"shape": "RHS", "width": 100, "depth": 100, "web": 5, "flange": 5, "designation": "SHS 100×100×5", "grade": "S355"}, …}`: its line runs from (0, 0, 0) to (6000, 0, 3450), through the middle of its section. A hanger plumb from [2000, 1000] at level 1 down to 900 mm under it has the profile `[[2000, 1000], [2000, 1000]]`, the `offset` 0 and the `end` `{"levelId": "level-1", "offset": -900}`.

*Note (informative).* The reference implementation has no `member` category: it records a brace as a beam with an `endRise`, which needs a run in plan of 10 mm or more, and its columns do not lean. To write and read members it adds the category, its `end`, and a body swept along the line as Part IV, 6.7 builds it, with its roll, taper, joins and axis as a beam's, and an IfcMember in its IFC print (Part VIII, 1.9).

### 6.3 Members that belong to some categories

An element MUST NOT carry these members unless its category is listed. The element category schemas set each forbidden member to `false`.

| Member | Categories | Meaning |
|---|---|---|
| `top` | `wall`, `shaft`, `stair` | a wall's top constraint and attachment (section 6.4); a shaft's or stair's top is only a `constraint` |
| `wallOutline`, `edges`, `framingTop`, `framingJoints`, `framingCorners`, `framingStuds`, `framingTees`, `framingStart`, `framingOffset` | `wall` | an elevation outline, edge details, timber framing rules (laid out by Part IV, 7.4) |
| `roomBounding` | `wall`, `column` | false when the element does not bound rooms |
| `turn` | `column` | turn about its centre (its reference line, Part IV, 6.5) in degrees, counter-clockwise in plan |
| `base` | `column` | base attachment (Part IV, 5.2) |
| `roll`, `endRise`, `taper` | `beam`; `roll` and `taper` on a `member` too (section 6.2.1) | roll about the reference line (Part IV, 6.5); rise of the end over the start; a linearly varying depth |
| `connection`, `joins` | `beam`, `column`; `joins` on a `member` too (section 6.2.1) | a schematic end plate; member joins (Part IV, 6.4) |
| `bottomPlate` | `door`, `window` | whether a framed wall's bottom plate runs through |
| `roof` | `roof` | slope-defining edges, overhangs, roof windows, the roofs it joins (`joinGroup`, below) |
| `joints`, `panels` | `slab`, `foundation`, `ceiling` | movement joints, panel division |
| `edgeAttachments` | `slab`, `foundation`, `ceiling`, `roof` | edges of the outline trimmed to a wall's face (section 6.7) |
| `room` | `room` | number, name, department, unit, finishes |
| `stair`, `railing` | `stair`, `railing` | shape and own numbers |
| `mep` | `pipe`, `duct` | heights, size, system and insulation (section 6.9) |
| `terminal` | `terminal` | its flow and its heat (section 6.9) |
| `bars` | `rebar` | a bar set |
| `sheet` | `mesh` | mesh sheets |
| `terrain`, `grading`, `propertyLine` | their categories | points, pads, setbacks |
| `generic` | `generic` | what the element stands for, its IFC class and its discipline, where they differ from its type's (section 6.6) |
| `notches` | `slabEdge` | stretches where its outside upstand is cut down; of 0.2 (Part IV, 4.6, rules 12 to 14), and in 0.1 a member the schemas do not know |

**A panel division.** `panels`, on a slab, a foundation or a ceiling, is `{width, direction?, start?, prefix?}`: a rule that divides the element into strips `width` mm wide (100 to 10 000), which a plan draws and numbers and a schedule counts. It is no geometry, and changes no body. The strips lie side by side along `direction`, in degrees counter-clockwise in plan from the model's x axis, 0 where it is absent: their widths add up along that direction from the point of the outline least far along it, moved on by `start` mm (0 where it is absent), and the lines between them run square to it. Each strip is labelled `prefix` (`P` where it is absent) followed by its number, counted from 1 along `direction`. So a floor of panels laid side by side along y, each spanning along x, has the `direction` 90.

*Note (informative).* 0.1 gave `panels` no meaning beyond the table's; the paragraph above says what the reference implementation reads, and was written on 28 October 2026 with open question 90. By that question's decision, 0.2 adds `mainDirection` on slabs, ceilings, foundations, roofs and walls, the direction their layers' grain is told from; a slab's runs square to its panels' `direction` (section 5.1.1, of 0.2).

Some members may be on any element, and mean something for a few categories only. Four of them, two members of the `roof` payload and the `terrain` payload's points mean this:

- `componentInverted`, on a `component`: `true` hangs its body down from its insertion height, from `z − height` to `z`, where it otherwise stands up from it, from `z` to `z + height` (`z` is its level's elevation plus its `offset`). It changes nothing for a component placed upright, by axes of its own or as a cast-in item.
- `axes`, on a `component`: `{x, y}`, two vectors in the model's axes, `x` not zero and `y` not parallel to it. They are the component's own frame, and take the place of its orientation and `upright`. Its x is `x` made a unit vector, its y is `y` made square to x and a unit vector, and its z is x × y, or y × x where the component is `flipped`. The frame's origin is the insertion point, the profile's first point, at its level's elevation plus its `offset`. A plate lies in its x–y plane: its outline is in x and y, and its thickness, the element's `height`, runs along z from the face, from 0 to `height`. A bolt group's bolts run along z, and a weld along x. With `axes`, the profile's second point, which rule 8 of section 6.1 still asks for, orients nothing, and `componentInverted` changes nothing.
- `miterEnds`, on a `slabEdge`: `true` mitres both its ends where its host's outline turns, so that it meets the edge element round the corner on the corner's bisector, and `false` keeps both ends square. Without it, an end is mitred where the edge round that corner carries an edge element that mitres by itself, one whose `miterEnds` is not `false` and whose type is no framing family (section 5.4), and where its own type is no framing family either. Part IV, 4.6, rule 6 says where a mitre is cut, and where an end stays square all the same.
- `horizontalOffset`, on a `slabEdge` or a `gutter`, in mm: moves its section square to its host's edge, a slab edge's in from the edge, into the host's outline, and a gutter's out from its roof's edge, added to its type's `gutterProfile.horizontalOffset`. A negative value moves it the other way. `verticalOffset`, on either, raises its section, a gutter's added to its type's `gutterProfile.verticalOffset` (Part IV, 4.6, rule 4, and 4.7, rule 4).
- `terrain.points`, on a `terrain`: points in plan with a height, `[x, y, z]`, z above its level's elevation plus its `offset`. Between them, the ground is the Delaunay triangulation of the points in plan, clipped to the element's outline (its `profile`): a triangulated irregular network, as LandXML's surfaces and IFC's `IfcTriangulatedIrregularNetwork` are. Where four or more points lie on one circle, any of the triangulations is the surface. Where two terrains overlap, which ground is on top is not defined in 0.1, since the order of elements has no meaning (Part III, 9, rule 3); a writer that wants one ground there clips the other terrain's outline.
- `roof.windows`, on a `roof`: its roof windows, each `{id, edgeId, center, width, length, openingOnly?, reveal?, typeId?}`. A roof window lies on the face of the edge `edgeId` names (Part IV, 4.4), centred over `center` in plan, `width` across the slope and `length` along it. `openingOnly` makes it an opening with no window in it, and `reveal` says how the opening runs through the roof. A covering and the infill under it that cut one opening carry the same window `id`. `typeId`, by the steward's decision of 21 October 2026, names the window's type: its product, and its U, g, frame fraction and light transmittance (section 5.5). It MUST name a type of the category `window` where it names a type of the model, and SHOULD name a type of the model (Part I, 7, rule 3). It belongs on the entry that holds the window, not on an `openingOnly` one.
- `roof.joinGroup`, on a `roof`: a name that the roofs of one group share. Roofs of one group join: where another roof of the group lies over a face, that part of the face is cut away, and a gutter along an edge that lies under another roof of the group is left out there.

A plate, a bolt group and a weld, components of a type of the family `plate`, `bolt` or `weld` (section 5), carry their own data in three members:

- `plate`, on a plate: its own shape over its type's. Its `outline`, three or more points in its own x and y in mm, around its insertion point, makes it a contour plate of that outline in place of the rectangle its type gives, and its `holes`, each as a plate type's hole is (section 5), go through it beside its type's. The holes of the bolts through it come with their bolt group.
- `bolts`, on a bolt group: its bolts, each the bolt its type holds (section 5), all along its z. `positions` are their centres in its x and y, in mm. `grip` is how far they pass through the parts along its z, from the face the head, or an anchor bolt's nut, bears on, at z = 0, to the face the nut bears on. `through` names the elements they pass through, each drilled for them, plates and members. `length` is a bolt's length from under its head, `hole` the holes' diameter, and `embed` an anchor bolt's reach past the grip into the concrete, or a rod's past the grip, its far nut included. Where a group gives no `length`, `hole` or `embed`, the reference implementation works them out from tables of ISO bolts and hole clearances that no record holds (open questions, 73).
- `weld`, on a weld: a fillet weld, its root along its x for its `width`, its length, and its legs along its y and z. `throat` is its throat, a, in mm, and its legs a·√2. Its `kind` is `fillet`, the one kind of 0.1, where it gives none. `site` is `true` for a weld made on site rather than in the shop, and `parts` names the one or two elements it joins.

What `componentInverted`, `axes`, `plate`, `bolts`, `weld` and `roof.joinGroup` do to a roof's or a component's body is no class-A rule (Part IV, 2): how a compiler derives it is reference behaviour in 0.1, and a component's frame, a plate, a weld's fillet and each bolt's shank become rules in 0.2 (open questions, 73 and 99), which Part IV, 7.6 gives. What `miterEnds`, `horizontalOffset` and `verticalOffset` do to a slab edge element's or a gutter's body, and where a roof of its group buries a gutter, are class-A rules since 26 October 2026 (Part IV, 4.6 and 4.7; open questions, 74 and 75).

Reference: `validateElement`, `validateSlabJoints`, `validateSlabPanels` and `slabPanels` (`src/core/elements/slabMarks.ts`), `validateRoof` (`roofs.ts`), `validateBars` (`bars.ts`), `validateSheet` (`meshSheets.ts`), `validateSiteElement` (`src/core/site/siteElements.ts`), `validateMemberJoins` (`memberJoins.ts`), `validateWallEdges` (`edgeDetails.ts`), `validatePlateType` and `validatePlateShape` (`components.ts`), `boltSpecSchema` and `validateBoltGroup` (`bolts.ts`), `validateWeld` (`welds.ts`); for the four members above, `plateFrame` (`components.ts`), `edgePlacement` (`edgePlacement.ts`), `gutterSolids` and `unburiedSegments` (`gutters.ts`) and `roofSolids` (`roofs.ts`).

*Note (informative).* The reference implementation refuses a `plate` on any element but a plate, a plate type with `thickness` or more than 200 mm thick, `holes` on a type of any family but `plate`, a hole that breaks a plate's edge or overlaps another, and a slotted hole shorter than it is wide; `bolts` on any element but a component of a bolt type, and a bolt type that holds no bolt; a bolt's `diameter` other than 10, 12, 16, 20, 24, 27, 30 or 36 mm, and `nuts` on any bolt but a rod; a group whose `through` names itself or no element, and a bolt or a rod too short for its grip and its nuts; and a `weld` on any element but a component of a weld type, and a weld type's element without one.

### 6.3.1 A slab on a sloped plane

*Part of 0.2. OpenEik 0.1 had it as the proposal `slopedSlab`: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), `plane` is a member a schema of 0.1 does not know, which a reader keeps (Part I, 10), and the slab is level. The section adds this one member and no file.*

By the steward's decision of 30 October 2026 (open questions, 111), asked by an analysis application: a slab may lie on a sloped plane, as a ramp, a sloped floor or deck, or a plate of an analysis model does. Its plane is given as a roof's slope-defining edge gives one, and Part IV, 4.2.1 builds it by the plane rule of a pitched roof (Part IV, 4.4), so that roofs and sloped slabs share one plane rule and one quantity rule.

1. **The plane.** A slab MAY carry `plane`, the plane of its top face. It gives the plane's gradient one way and its direction one way:
   - its gradient as `slope`, in degrees from 0 to 75, as a roof edge's slope is (section 6.3); or as `rise` over `run`, both in mm, `run` more than 0, its slope atan(`rise`/`run`), which MUST NOT be more than 75 degrees;
   - its direction as `edgeId`, an edge of the slab's outline (Part I, 6, rule 6; section 6.1, rule 10), from whose line its top rises into the outline, as a roof's top rises from a slope-defining edge (Part IV, 4.4, rule 1); or as `fall`, the direction in plan in which its top falls, in degrees counter-clockwise from the model's x axis.

   It carries exactly one of `slope` and `rise` with `run`, and exactly one of `edgeId` and `fall`.
2. **Where it lies.** The slab's `elev(levelId) + offset`, z1, is its top's height along the line of the edge `edgeId` names, or at its profile's first point where it gives `fall`. With σ its slope, its top at a point p in plan is z(p) = z1 + tan σ · (n · (p − a)), where a is the edge's start and n its unit normal into the outline, or z(p) = z1 − tan σ · (f · (p − p0)), where f = (cos `fall`, sin `fall`) and p0 is the profile's first point.
3. **Either form is one plane.** Each gives the plane's slope, the direction in plan in which its top falls, −n or f, and its top's height at the profile's first point, z(p0). A reader derives these from either form, and so either form from the other where it can be written: an edge form where the outline has an edge square to the fall.
4. **Its top face.** The plane is the slab's top face, as a level slab's top is `elev(levelId) + offset` (Part I, 4) and a pitched roof's planes are its top (Part IV, 4.4). Its thickness, its type's layers' total or else its `height` (Part IV, 4.2, rule 1), is measured square to the plane, as a roof's is (Part IV, 4.4, rule 4), and its layers lie under it, top first.
5. **A level slab** has no `plane`, or one of slope 0: its top is z1 everywhere, and nothing of it changes.
6. **Which slabs.** Only a slab carries `plane`. A validator reports `plane` on an element of another category, an `edgeId` that names no edge of the outline, and a `rise` over `run` steeper than 75 degrees (`slab-plane`). A slab whose plane slopes, of a slope over 0, MUST NOT host a slab edge element in 0.2 (`slab-plane-edge`; Part IV, 4.2.1, rule 6).
7. **Its grain.** A sloped slab's main direction (section 5.1.1), from its panels' `direction` or its `mainDirection`, is a direction in plan projected vertically onto its plane, as a roof's `mainDirection` is (section 5.1.1, rule 4). A panel division (section 6.3) stays one in plan.

A ramp 6000 mm long and 2000 mm wide, `[[0, 0], [6000, 0], [6000, 2000], [0, 2000]]` on level 0, elevation 0, with the offset 0, rising 300 mm from its west edge, its fourth, `ramp-a:edge:3`, to its east edge, has `"plane": {"edgeId": "ramp-a:edge:3", "rise": 300, "run": 6000}`: its top is z = x/20, from 0 at x = 0 to 300 at x = 6000. The same plane by its fall is `{"fall": 180, "rise": 300, "run": 6000}`, since the profile's first point lies on the low edge. A triangular landing whose fall runs square to none of its edges has only the second form.

*Note (informative).* The reference implementation has no sloped slab: its slabs are level, and a sloped plate is written as a roof. To write and read one it adds the member `plane`, the body of Part IV, 4.2.1, built as its pitched roofs' layers are (`roofSolids`), with its quantities, and its plane wherever its tools read a slab's top.

### 6.4 A wall's top

`top` is `{constraint, attach?, extendExteriorLayers?, cutRoofId?, cutRoofFace?}`:

- `constraint` is `"unconnected"` (the top is `base + height`) or `{levelId, offset}` (the top is that level's elevation plus `offset`, which MUST lie above the base).
- `attach` is `{hostId, face, offset?, mode?}`: the wall's top conforms to a face of a roof, floor, foundation or ceiling: its `"underside"`, its `"top"`, or `{layerId}`, the underside of one of the host's layers. `mode` is `"raking"` (the default: the top follows the face) or `"level"` (flat at the lowest point of the face over the wall's core).
- `height` and `wallOutline` keep their unconnected values while a wall is attached.

The derivation is in Part IV, section 5.3.

Writers MUST NOT write the legacy members `topRoofId`, `topCeilingId` and `roofCutId`. A reader SHOULD read them as `top` (`topRoofId` and `topCeilingId` as an attachment to that host's underside, `roofCutId` as `cutRoofId`).

Reference: `wallTopSchema`, `legacyWallTop` in `src/core/elements/wallTop.ts`; `normalizeElement` in `src/core/elements/edges.ts` folds the legacy members into `top` on load; `run` refuses them in patches.

### 6.5 Hosted openings

A door or window names its wall in `hostId`. `along` is the distance in plan from the wall's first profile point to the opening's near edge; `width` and `height` are its size; `offset` is the height of its bottom above the wall's base. `flipped` turns it to face its wall's interior rather than its exterior (section 6.8), and `handFlipped` mirrors its handing, and the shape its type's `outline` gives it, along its wall (Part IV, 4.3).

**How it opens.** By the steward's decision of 26 October 2026 (open questions, 71), as the reference implementation reads these members. A door's or a window's type with an `openingFamily` (section 5) says how its leaves or its lights open: `operable` whether they open, `hinge` the side they hinge on, `swing` the side they open to, and, for a door, `operation` how a leaf moves. With the element's `flipped` and `handFlipped`:

1. **The hinge side.** A leaf or a light hinges at the end of its opening nearer its wall's first point, its start, at `along`, where `hinge` is `left`, and at its other end, at `along + width`, where it is `right`; `handFlipped` swaps the two. `top` and `bottom` hinge a window's light at its head or its sill, whatever `handFlipped` says.
2. **The side it opens to.** It opens to its wall's exterior, the left of the wall's baseline (Part I, 5.1), where `swing` is `outward`, and to its interior where `swing` is `inward`; `flipped` swaps the two.
3. **Several leaves or lights.** A door of the kind `door` has `lightsX` leaves side by side, and one of the kind `garageSideHinged` its `panels`. Of `n` leaves counted from the opening's start, leaf `i` (from 0) hinges at the start where `i < n/2` and at the end otherwise, whatever `hinge` and `handFlipped` say. A window's lights stand in its grid, `lightsX` across and `lightsY` up, or as its `mullions` divide it; where they hinge at a side and stand in more than one column, column `i` of `n` hinges at the start where `i < n/2` and at the end otherwise.
4. **How a door's leaf moves**, its `operation`, on a family of the kind `door`: `swing`, or none, turns it on its hinges to the side it opens to. `slide` runs it along its wall: a single leaf hangs on the face of the wall on the side it opens to and travels its own width towards its hinge side; of several leaves, counted from the hinge side, the first stays, the second slides over it towards the hinge side, the third stays, and so on, on two tracks 6 mm apart, each leaf that slides on the track on the side the door opens to. `pocket` slides a leaf into the wall on its hinge side, and parts several leaves, each into the wall at the end of its own half (rule 3).
5. **A family that does not open.** Where `operable` is `false`, its leaves and its lights do not open: rules 1 to 4 say nothing of it.
6. A family of the kind `door` or `garageSideHinged` SHOULD hinge `left` or `right`: one that hinges at its `top` or `bottom` has no operation in 0.1. `operation` SHOULD be `slide` or `pocket` only on a family of the kind `door`, and a reader ignores it on another. A validator SHOULD warn of both.

A garage door of the kind `garageSectional` runs up in its sections, one of the kind `garageRoller` rolls up, and one of the kind `garageFolding` folds its `panels` to both ends; `hinge` and `operation` say nothing of them. Part VIII, 1.2 maps each operation to IFC4's. The frame, the leaves or sashes and the glass become class-A bodies in 0.2 (Part IV, 4.3 and 4.3.1); a body drawn for a print, a clash check or a quantity is then closed, `open`, which the element schema allows, being a pose for showing.

*Note (informative).* The reference implementation draws a door's leaves whatever `operable` says, and a single leaf of a door hinged at its `top` or `bottom` at the opening's start, mirrored by `handFlipped` in a plan and not in 3D. Since Stomra 727 step 4 (merged on 28 October 2026) its IFC writes a door's operation as Part VIII, 1.2 maps it, with `doorOperation` of `@openeik/tools`; before, it wrote a sliding door's operation type as SLIDING_TO_LEFT where the leaf slid towards the opening's start and SLIDING_TO_RIGHT towards its end, whichever side it opened to (open questions, section 2, row 36).

Reference: `openingSolids` (`hinge`, `swingToward`) and `pocketSide` in `src/core/elements/openings.ts`; `slidingLeaves`, `leafSlides` and `slideSide` in `src/core/elements/slidingDoors.ts`; `lightSpans` in `src/core/elements/mullions.ts`.

### 6.6 Generic elements

The category `generic` is for a thing that no other category fits: a piece of equipment, a lift, a sculpture, signage, temporary works, a scanned part of an existing building. It is a disciplined way out: a thing that a category fits is modelled in that category, and nothing is made to look like something else, a box component or a piece of furniture standing in for a lift.

1. **What it stands for.** A generic type MUST carry the payload `generic`, `{standsFor, ifcClass?, discipline?}`:
   - `standsFor`, required: a non-empty text that says what the thing is (`passenger lift`, `sculpture`, `scaffold tower`);
   - `ifcClass`: the IFC entity a bridge exports it as, written as IFC writes it (`IfcTransportElement`); absent, `IfcBuildingElementProxy`;
   - `discipline`: the discipline folder it goes in, one of `architecture`, `structure`, `systems` and `site` (Part III, 4.1); absent, `architecture`.

   A generic element MAY carry `generic` too, with any of the three members: each member it gives wins over its type's.
2. **Its shape is declared geometry**, and nothing else: its type's declaration, in the frame of a category placed at a point, its own, or both, under the rules of section 12.1. A generic element MUST have declared geometry, its own or its type's, so it has at least one body, and its declaration is authoritative for its `envelope`. OpenEik defines no other geometry for it.
3. **Its placement.** Its `profile` is its insertion point, or its insertion point and a second, distinct point that gives its orientation, as a component's (section 6.1, rule 8); with one point, its x axis is the project's. `elev(levelId) + offset` is its base, z = 0 of its frame (Part I, 4). Its `width` and `height` are not used for its shape.
4. **Not hosted, cutting nothing.** A generic element has no `hostId`, and in 0.1 it cuts no other element.
5. **A category that fits comes first.** Writers and agents SHOULD use a category that fits before `generic`. A validator MAY warn when a generic element looks like an element of a category that fits (as the model-quality warnings of Conformance, 2 do for other categories), and an agent host SHOULD say how many of a model's elements are generic.
6. **Its size.** A record's declared geometry SHOULD hold fewer than 10 000 faces in all, counting each face of a polyhedron or a mesh, n + 2 for a prism of n points, and 3 for a cylinder or a rod. Larger meshes, kept as binary assets beside the records, are planned and are not part of 0.1.
7. **Promotion.** When a category comes to fit (a later version adds one, or the thing is understood), the element moves to it by a batch of a `delete` and a `create` with the same id (Part V, 7): each command sees the model as the one before it left it, so the id is kept and no other command kind is needed. As any delete does, the `delete` removes what depends on the element (Part V, 5).
8. From 0.2 a generic type or element MAY also carry classification references (section 11).
9. **A person.** A figure of a person placed in a model, to show its scale or to check the free height over a place, is a `generic` element whose type stands for a person: the type's `generic.standsFor` is `person`, and its shape is declared geometry (rule 2), a body as tall as the person it stands for. There is no category `person` (decided on 30 October 2026; open questions, section 4, D9). How an application draws such a figure, a model of a body or a pose, is its own, in an extension member (Part I, 11.2).

A generic type standing for a passenger lift, and an element of it (compacted; on disk each member is a line):

```json
{
  "category": "generic", "color": "#8a9199", "height": 2600, "id": "type-lift-8",
  "material": "Steel", "name": "Passenger lift, 8 persons", "width": 1600,
  "declaredGeometry": {
    "authoritativeFor": ["envelope", "clearances"],
    "clearances": [{"body": {"bottom": 0, "kind": "prism", "profile": [[0, -1500], [1600, -1500], [1600, 0], [0, 0]], "top": 2100}, "id": "landing", "purpose": "operation"}],
    "envelope": [{"bottom": -1200, "kind": "prism", "profile": [[0, 0], [1600, 0], [1600, 2100], [0, 2100]], "top": 3500}],
    "level": "interface",
    "tolerances": {"default": 10}
  },
  "generic": {"discipline": "systems", "ifcClass": "IfcTransportElement", "standsFor": "passenger lift"}
}
```

```json
{
  "category": "generic", "height": 2600, "id": "lift-a", "levelId": "level-0", "name": "Lift A",
  "offset": 0, "profile": [[4000, 2000], [4000, 3000]], "revision": 0, "typeId": "type-lift-8", "width": 1600
}
```

The element stands at `[4000, 2000]` on level 0, its x axis pointing along +y; the type's envelope reaches 1200 mm into the pit below its base, and the landing in front of its doors must stay free. The type goes in `types/systems/generic/type-lift-8.json`, the element in `systems/level-0/generic/lift-a.json`.

### 6.7 Outline edges trimmed to a wall's face

`edgeAttachments` is a list of `{edgeId, hostId, face}`. Each entry trims an edge of a slab's, foundation's, ceiling's or roof's outline to a face of a wall:

- `edgeId` names the edge. Edge `n` runs from the outline's point `n` to the next; it is named by `edgeIds[n]`, or `<id>:edge:<n>` where the element has no `edgeIds` (Part I, 6.6).
- `hostId` names the wall.
- `face` is `"interior"` or `"exterior"`: the exterior face is on the left of the wall's directed baseline (Part I, 5.1), the interior face on its right.

The outline stays as it is recorded. The trim is derived (Part IV, 4.5), so the edge follows the wall when the wall moves, turns or changes thickness, and neither record changes.

1. Each `edgeId` MUST name an edge of the element's outline, and an edge MUST NOT be named twice. A round footing (section 6.1, rule 3) has no edges to name.
2. `hostId` MUST name a wall.
3. The edge MUST be parallel to the wall's baseline within 5°.
4. Each end of the edge MUST lie no more than 1 mm beyond the face, on the side away from the wall. An attachment trims an edge back to the face; it never moves an edge out to meet it.
5. The outline trimmed at every face its attachments name (Part IV, 4.5) MUST keep an area of at least 100 mm², as an outline must (section 6.1, rule 3).

A ground slab whose south edge is trimmed to the interior face of the south wall (compacted):

```json
{
  "category": "slab", "edgeAttachments": [{"edgeId": "slab-ground:edge:0", "face": "interior", "hostId": "wall-south"}],
  "height": 250, "id": "slab-ground", "levelId": "level-0", "name": "Ground floor", "offset": 0,
  "profile": [[0, 0], [10000, 0], [10000, 7000], [0, 7000]], "revision": 1, "typeId": "type-slab-250", "width": 250
}
```

The wall runs from `[10000, 0]` to `[0, 0]`, so its interior face is on its right, the north side; its type is 300 mm thick, so the face is the line y = 150. The slab's edge 0, from `[0, 0]` to `[10000, 0]`, lies on the wall's centreline. The slab's bodies start at the face and measure 10 m by 6.85 m in plan. A wall 400 mm thick moves the face to y = 200, and one 200 mm thick to y = 100; the slab follows, and neither record changes.

*Note (informative).* Rule 4 is why the slab above records its edge on the wall's centreline. An edge recorded on the face itself follows a wall that grows thicker. A thinner wall would leave that edge short of its face. Since Stomra 642 (live since 23 October 2026, `main` `83aa3e13`) the reference implementation then moves the edge of the recorded outline onto the new face, in the command that makes the wall thinner: the edge's ends slide along the lines of the edges beside it, which keep their directions (beside an edge within 5° of it, the shared point moves square to the face), and the outline keeps its points and its edge ids. It still refuses the change, naming the edge, the element and the wall, where moving the edge would change a pitched roof's plane (the edge, or one beside it that it would turn, defines a slope), on a barrel vault, and on a pinned element. Before 642 it refused every such change. An edge recorded on the centreline, or past the wall, follows either way, and rule 4 holds throughout. The reference command `wall.flip`, which turns a wall's baseline round, also swaps the face of every attachment to the wall, so that each edge keeps to the face it was trimmed to.

Reference: `edgeAttachmentSchema`, `validateEdgeAttachments` and `footprintTrims` in `src/core/elements/wallTop.ts`; `validateFooting` in `src/core/elements/footings.ts`; the `edge.attach` and `wall.flip` branches of `run` in `src/core/model/store.ts`.

### 6.8 Which way an element faces

By the steward's decision of 21 October 2026. An energy balance, a daylight study or a sun study sums walls, doors and windows by the way they face: their facing, a direction in plan, and its bearing, that direction's angle from true north.

1. A wall faces its exterior: the left of its directed baseline (Part I, 5.1). Its facing is its left normal `n = (−u_y, u_x)`, where `u` is the baseline's direction (Part IV, 3).
2. A door or a window faces its host wall's exterior, `n`. With `flipped: true` it faces the wall's interior, `−n`.
3. A roof window faces down the face it lies on, the plane of the edge `edgeId` names (Part IV, 4.4, rule 1). Its facing is that edge's normal out of the roof's outline, `−n_i`, and its tilt is the edge's slope `σ_i`. Where that edge defines no slope, the window lies level: its tilt is 0, it faces straight up, and it has no bearing.
4. A facing `f` has the bearing `(atan2(f_x, f_y) · 180/π − trueNorth) mod 360`: its angle in degrees, from 0 to less than 360, clockwise from true north, where `trueNorth` is the project's `site.trueNorth`, true north's angle clockwise from plan +Y (section 2).
5. Without `site.trueNorth`, plan +Y is north: bearings are taken with `trueNorth` 0. A validator SHOULD warn when such a model holds a door, a window or a roof window, whose bearings then rest on it.

In the one-storey house, the south wall runs from `[10000, 0]` to `[0, 0]`: its facing is `(0, −1)`, south in plan. Its site gives `trueNorth` 15, so the wall's bearing is 180 − 15 = 165°, and so is the bearing of each of its windows that is not flipped.

*Note (informative).* By rule 1 a wall drawn the wrong way round faces into the building, as its layers do: the reference implementation finds such walls (`facadeInWalls`), and its `wall.flip` turns a wall and keeps its doors and windows facing as they were. An application that sums glazing by orientation groups the bearings as it needs, by quarter or by eighth of the compass.

Reference: `exteriorFacing = !e.flipped` in `src/core/elements/openings.ts`; `facadeInWalls` in `src/core/elements/wallFacing.ts`; `roofFaces` in `src/core/elements/roofs.ts`.

### 6.9 Heating, ventilation and plumbing

By the steward's decisions of 23 and 24 October 2026, from the reference implementation's plan 606 (open questions 45 to 51). Pipes, ducts and terminals (section 6.2) are the building services of a model. Their systems, a run's insulation, a heating terminal's heat and the components in a pipe or a duct are data of their records; their views and drawings are section 13's, and their bodies Part IV, 12's.

**Systems.** A run's `mep.system`, a type's `pipe.system`, `duct.system` and `terminal.system`, and the `system` of a terminal type's connection or outlet are systems of one enumeration:

| System | What it carries | Falls by gravity | Carries air |
|---|---|---|---|
| `sewage` | waste water | yes | no |
| `storm` | storm water | yes | no |
| `greaseWaste` | waste water that carries grease, from a kitchen | yes | no |
| `oilWaste` | waste water that carries oil, from a garage or a workshop | yes | no |
| `coldWater` | domestic cold water | no | no |
| `hotWater` | domestic hot water | no | no |
| `hotWaterCirculation` | domestic hot water's circulation | no | no |
| `heating` | heating water | no | no |
| `districtHeating` | the primary water of district heating, supply and return as one system | no | no |
| `brine` | brine, for cooling or a heat pump's collector | no | no |
| `compressedAir` | compressed air | no | no |
| `gas` | fuel gas | no | no |
| `supplyAir` | supply air | no | yes |
| `extractAir` | extract air | no | yes |
| `outdoorAir` | outdoor air | no | yes |
| `exhaustAir` | exhaust air | no | yes |
| `kitchenExhaust` | a kitchen hood's exhaust | no | yes |

A run's `mep.number`, 1 to 99 and 1 when absent, is its system's number on the drawings: the heating system 20, which Swedish drawings write VS20. A system's code on drawings (KV for cold water, VS for heating, L for compressed air), its name and its colour are not data of the standard (section 8, rule 3): an application gives them, and a project may give its own in its `standards`, an application value (section 9.2). Whether a system falls by gravity, or carries air, is the system's, as the table says.

**Insulation.** A run's insulation is a code that a drawing writes after the run's size, and a thickness in mm that its body takes (Part IV, 12.1, rule 3). A pipe type and a duct type give them alike:

| Member | Meaning |
|---|---|
| `pipe.insulationCode`, `duct.insulationCode` | the type's insulation code, 1 to 12 characters, a national value: `R5DF40-C` on a Swedish pipe drawing (class R5, hanging D, material F, 40 mm, surface C), `B11` for a duct's fire insulation of class EI15 |
| `pipe.insulation`, `duct.insulation` | the insulation's thickness in mm, from 0 to 200 |

A run's own `mep.insulation` gives its own, where it differs from its type's, each member optional:

| Member | Meaning |
|---|---|
| `code` | its insulation code, as a type's |
| `thickness` | its insulation's thickness in mm, from 0 to 200, beside its own `code` |
| `none` | `true`: the run has no insulation, whatever its type gives |
| `bti` | deprecated (rule 4): a pipe's code by its parts |

A run's insulation code is its own `code`, else its type's `insulationCode`; its thickness its own `thickness`, else its type's `insulation`, and 0 without either; with `none` it has none. What a code means, its kind, its text and a thickness, is the project's legend, an application value: a run's thickness is the model's, never only a code table's.

**A code by its parts, deprecated.** The reference implementation writes a pipe's code by the parts of one national code, a type's `pipe.bti` and a run's `mep.insulation.bti`: `{class, hanging, material, thickness, surface?}`, each part 1 to 8 characters and a national value, the thickness in mm from 1 to 200. `{"class": "R5", "hanging": "D", "material": "F", "surface": "C", "thickness": 40}` is the code `R5DF40-C` and 40 mm. 0.1 reads it, deprecated (Part I, 9.6): rule 4.

The heating pipe of this section's examples, a copper pipe on level 0, 22 mm, in the heating system 20, insulated with 30 mm of the code `R4SF30`:

```json
{
  "category": "pipe",
  "height": 22,
  "id": "pipe-heating",
  "levelId": "level-0",
  "mep": {
    "insulation": {
      "code": "R4SF30",
      "thickness": 30
    },
    "number": 20,
    "size": 22,
    "system": "heating",
    "z": [2400, 2400, 2400]
  },
  "name": "Heating supply",
  "offset": 0,
  "profile": [
    [1000, 1000],
    [5000, 1000],
    [5000, 4000]
  ],
  "revision": 0,
  "typeId": "type-pipe-heating-cu",
  "width": 22
}
```

**Heat.** A heating terminal, a radiator, a towel rail or a unit heater, has a power, and so a water flow at its design temperature difference (Part IV, 12.7). A terminal type's `terminal.power` is its power in W, from 1 to 500 000: what its elements give, unless one gives its own. A terminal's own `terminal`, beside its `flow`, gives:

| Member | Meaning |
|---|---|
| `power` | its power in W, from 1 to 500 000, where it differs from its type's |
| `deltaT` | its design temperature difference in K, from 1 to 80: the difference between its supply and its return water at which its flow is worked out |
| `kv` | its valve's flow coefficient, kv, in m³/h at a pressure drop of 1 bar, from 0 to 1000 |
| `preset` | its valve's presetting, as its maker writes it, 1 to 12 characters, a plain value |

A terminal's power is its own `terminal.power`, else its type's. A unit heater, a fan that blows air over a heating coil, is a terminal type of the kind `unitHeater`. A heating system's supply and return temperatures, from which an application takes a terminal's design difference where the terminal gives none, are the project's `standards`, an application value (open question 47).

The unit heater of a workshop, 3500 W at a design difference of 15 K; its type gives 3900 W:

```json
{
  "category": "terminal",
  "height": 500,
  "id": "unit-heater-a",
  "levelId": "level-0",
  "name": "Unit heater",
  "offset": 2500,
  "profile": [
    [6000, 3000],
    [6000, 2000]
  ],
  "revision": 0,
  "terminal": {
    "deltaT": 15,
    "power": 3500
  },
  "typeId": "type-unit-heater",
  "width": 600
}
```

**Where a terminal stands.** A terminal's `profile` is its point in plan, and, where it gives a second point, the way it faces: from the first point toward the second. A terminal whose profile is one point faces plan +y. Its `offset` is the height of its base over its level (Part I, 4): a terminal stands from there up, whatever its mount, as the reference implementation places it (a ceiling outlet's base is where it hangs from the ceiling, a duct part's that names no duct half its height under the duct's axis). Its body is its declared geometry, its own or its type's, where it has one, else a box of its type's size (Part IV, 12.6). A component in a pipe or a duct stands where its run puts it instead (below).

**Components in a pipe or a duct.** A **component** is a valve, a pump, a strainer, a meter, an expansion vessel, a backflow preventer or an air vent in a pipe, or a damper, a silencer, a fan or a flow regulator in a duct: a terminal whose type's `terminal.mount` is `pipe` or `duct`, hosted on its **run**, the pipe or the duct it is in, at a distance along it. It follows its run. It has no connections of its own: the run stays one run through it, for its joins, its body and its quantities (Part IV, 12), and for what is derived from them. A component in a pipe is of one of these kinds, or a `waterMeter`:

| Kind | The component |
|---|---|
| `isolatingValve` | an isolating valve, such as a ball valve |
| `checkValve` | a check valve |
| `balancingValve` | a balancing valve, which measures its flow and is preset |
| `controlValve` | a control valve with its actuator |
| `safetyValve` | a safety valve |
| `pump` | a circulation pump |
| `strainer` | a strainer |
| `energyMeter` | a heat or energy meter |
| `expansionVessel` | an expansion vessel |
| `backflowPreventer` | a backflow preventer |
| `airVent` | an air vent |

A component in a duct, by the steward's decision of 24 October 2026 (open question 51), is of one of these kinds, which Part VIII, 1.1 exports as the IFC4 classes the table gives:

| Kind | The component | IFC4: class, predefined type |
|---|---|---|
| `damper` | a damper that controls or shuts off the air flow | IfcDamper, CONTROLDAMPER |
| `fireDamper` | a fire damper | IfcDamper, FIREDAMPER |
| `smokeDamper` | a smoke damper | IfcDamper, SMOKEDAMPER |
| `flowRegulator` | a flow regulator, which holds the air flow through it at the flow it is set to | IfcDamper, BALANCINGDAMPER |
| `silencer` | a silencer | IfcDuctSilencer, ROUND in a round duct and RECTANGULAR in a rectangular one |
| `ductFan` | a fan in the duct | IfcFan, USERDEFINED, its ObjectType `INLINE` |

The kinds may have other mounts too: a pump on a floor is a `pump` mounted on the floor, and stands where its profile puts it, as any terminal does. A component in a pipe is its type's `terminal.width` across the pipe, `terminal.depth` along it and `terminal.height` up; a component in a duct is its size across (below) across the duct and up, and its type's `terminal.depth` along it (Part IV, 12.6). A component's type's `code` is the code its elements are tagged with, unless an element gives its own `mark`. Its `system` is not read: a component's system is its run's, with the run's number.

| Member | Meaning |
|---|---|
| `hostId` | its run: its pipe, or its duct (rule 6) |
| `along` | its distance from its run's first point, in mm, along the run's points in 3D: a vertical piece counts, as in the run's length (Part IV, 12.4, rule 2). At least 0; a component with no `along` is at 0 |
| `levelId`, `profile`, `offset` | written from its run (below) |
| `width`, `height` | in a pipe, its type's; in a duct, its size across, written from its duct (below) |
| `mark` | optional: its own tag, such as `V201` |
| `terminal` | optional: a balancing valve's `kv` or `preset` |

**Where a component stands.** Let the run's points be `P_0 … P_(m−1)` (Part IV, 12.1, rule 1), and `s = min(along, L)`, where `L` is the run's length. The component's **point** is the point at the distance `s` from `P_0` along the run's points: on the piece where it falls, at the share `s′ / l` of the piece from its start, `s′` the distance left and `l` the piece's length. Its **way** is the direction in plan of that piece; on a vertical piece, the way of the last piece before it that runs more than 1 mm in plan; and `+y` where none does. Its `levelId` is its run's. Its `profile` is two points: its point in plan, and the point 1000 mm from it along its way. Its `offset` is its point's height above that level less half its body's height, its type's `terminal.height` in a pipe and its size across up in a duct, so that its body is centred on the run's axis. The run and `along` are the intent; the three are the writer's copy of where they put it, as a hosted railing's line is (section 12.2, rule 4).

**Its size across in a duct.** A component in a duct is at least as large across as its duct, and stands 10 mm proud of it on each side, as a casing does. Let `W` and `H` be the duct's outer width and height, its size with its insulation (Part IV, 12.1, rule 3; a round duct is `W` across both ways). The component's **size across** is the larger of its type's `terminal.width` and `W + 20` wide, and the larger of its type's `terminal.height` and `H + 20` high, in mm. So one damper type fits a duct of any size, and a type that is larger, a silencer's casing or a fan's box, keeps its own size. Its length along the duct is its type's `terminal.depth`. The duct and the two types are the intent; the component's `width` and `height` are the writer's copy of its size across, as its place is.

**When its run changes**, a component keeps its `along`, and stands where the changed run puts it: a writer rewrites its `levelId`, `profile` and `offset` in the same command, and in a duct its `width` and `height` (rule 8). When the run gets shorter than its `along`, it stands at the run's end, and its `along` stays as it was: the run lengthened again puts it back. A command that moves a component's point and leaves its `along` slides it along its run. When its run is deleted, it is deleted with it, as every hosted element is (Part V, 5, rule 1).

**A duct part that names no duct.** A terminal whose type mounts on a duct and that gives no `hostId` is a terminal like any other: it stands where its `profile` and `offset` put it, its body is its type's box from its base up (Part IV, 12.6), and it does not follow a duct. The reference implementation placed its silencers, fans and fire dampers so before its work 676, and leaves them where they were put: such a model stays valid, and a writer hosts one by giving it a `hostId` and an `along`. Which systems it goes with in a view is section 13.1's.

A valve 2000 mm along the heating pipe above, on its first piece, which runs +x at 2400 mm above level 0; its type is 70 mm high:

```json
{
  "along": 2000,
  "category": "terminal",
  "height": 70,
  "hostId": "pipe-heating",
  "id": "valve-a",
  "levelId": "level-0",
  "mark": "V201",
  "name": "Isolating valve",
  "offset": 2365,
  "profile": [
    [3000, 1000],
    [4000, 1000]
  ],
  "revision": 0,
  "typeId": "type-isolating-valve",
  "width": 60
}
```

Its type:

```json
{
  "category": "terminal",
  "color": "#b08d57",
  "height": 70,
  "id": "type-isolating-valve",
  "material": "Brass",
  "name": "Isolating valve",
  "revision": 0,
  "terminal": {
    "code": "V101",
    "depth": 80,
    "height": 70,
    "kind": "isolatingValve",
    "mount": "pipe",
    "width": 60
  },
  "width": 60
}
```

A damper 1500 mm along a rectangular supply air duct, `duct-supply`, 400 mm wide and 200 mm high without insulation, whose first piece runs +x from `[1000, 5000]` at 2700 mm above level 0. Its type is 145 mm across and 145 mm high, smaller than the duct, so its size across is the duct's and 10 mm each side, 420 mm by 220 mm, and its base is 110 mm under the duct's axis:

```json
{
  "along": 1500,
  "category": "terminal",
  "height": 220,
  "hostId": "duct-supply",
  "id": "damper-a",
  "levelId": "level-0",
  "mark": "D201",
  "name": "Damper",
  "offset": 2590,
  "profile": [
    [2500, 5000],
    [3500, 5000]
  ],
  "revision": 0,
  "typeId": "type-damper",
  "width": 420
}
```

Its type:

```json
{
  "category": "terminal",
  "color": "#9aa0a6",
  "height": 145,
  "id": "type-damper",
  "material": "Steel",
  "name": "Damper",
  "revision": 0,
  "terminal": {
    "code": "D101",
    "depth": 200,
    "height": 145,
    "kind": "damper",
    "mount": "duct",
    "width": 145
  },
  "width": 145
}
```

1. A run's `mep.system`, a type's `pipe.system`, `duct.system` and `terminal.system`, and the `system` of a terminal type's connection or outlet MUST be a system of the table above. The schemas state this rule.
2. An insulation code, a type's `insulationCode` or a run's `code`, and each part of a `bti` are national values: a validator MUST NOT refuse one because a code list does not hold it (section 8, rule 4). The schemas mark them `"x-eik-values": "national"`.
3. A run's `mep.insulation` that gives `none` SHOULD give nothing else. A duct's SHOULD NOT give `bti`. A run that gives its own `code` SHOULD give its own `thickness`.
4. `bti` is deprecated (Part I, 9.6): a writer SHOULD NOT write it, and SHOULD write the code and the thickness instead; a writer that does removes the `bti`. A `bti` MUST give `class`, `hanging`, `material` and `thickness`. A reader reads one as a code and a thickness: its `thickness` is the thickness, and its parts written together are the code: `class`, `hanging`, `material`, the thickness in mm as a whole number, and then `-` and `surface` where it has one. Where a type or a run gives a `bti`, it stands in place of the code and the thickness beside it, as the reference implementation reads it. A validator SHOULD warn about each `bti` a model holds, naming the code and the thickness to write instead.
5. `terminal.power`, `terminal.deltaT`, `terminal.kv` and `terminal.preset` MUST be within the ranges of the tables above. The schemas state this rule.
6. A terminal whose type's `terminal.mount` is `pipe` MUST name a pipe of the model in `hostId`. A terminal whose type's `terminal.mount` is `duct` and that gives a `hostId` MUST name a duct of the model there; one that gives none is a duct part that names no duct (above).
7. A component's `along` SHOULD be at most its run's length. A component whose `along` is past its run's end stands at the end.
8. A component's `levelId`, `profile` and `offset` SHOULD be where its run and `along` put it: its level the run's, its point within 1 mm in plan, its way within 1°, its `offset` within 1 mm. The `width` and `height` of a component in a duct SHOULD be its size across, each within 1 mm. A writer rewrites them in the same command whenever the run, `along` or the component's type changes. A reader MUST follow the run and `along`, and the size across, where they disagree.
9. A terminal type whose `terminal.mount` is `pipe` or `duct` SHOULD NOT have `connections` or `outlets`: a component is in its run, which stays one run through it, and no run's end joins it.

Reference: `mepSystems` and `systemIds` in `src/core/elements/mepSystemList.ts` (Stomra work 610); `btiSchema` and `runInsulationSchema` in `src/core/elements/mepInsulationSchema.ts`, and `runInsulation` in `src/core/elements/mepInsulation.ts` (615); `terminalHeatTypeFields` and `terminalHeatFields` in `src/core/elements/mepHeatSchema.ts` (616); `onPipe`, `onDuct`, `alongRun`, `alongOf`, `componentBody` (the size across, with `DUCT_COLLAR`), `placedOnRun` and `followedRunComponents` in `src/core/elements/runComponents.ts`, which was `pipeComponents.ts` (633, 676); `componentMount` and `componentHost` in `src/core/elements/componentHost.ts` (676); `validateTerminal` (rule 6), `runOuter` and `terminalSystem` in `src/core/elements/mep.ts`; and the types in `src/core/elements/mepComponentPresets.ts` and `mepPresets.ts` (633, 676). Plan 606 was built in five waves, on Stomra's `main` from 21 to 22 October 2026 (merge requests !240, !244, !249, !260 and !280); this text was written from its `main` at `43384047`. Components in a duct are its work 676, live since 24 October 2026 (`main` `f02ba07e`); their text was written from its `main` at `b7318113`.

*Note (informative).* The reference implementation names, codes and colours each system in `mepSystems`, and a project's own code, name and colour for one, `project.standards.mep.systems`, win (`systemLook`). Until Stomra 669 (live since 24 October 2026, `main` `2ea04ddd`) it wrote a pipe's insulation code by its parts (`bti`), and a duct's code with no thickness of the run's own, drawing a duct whose code has a thickness in a table that no model holds (`K25`, a condensation insulation, is 50 mm there) that much larger (open questions, section 2, rows 27 and 28). Since 669 it writes the code and the thickness, a duct's thickness taken from its project's table (`project.standards.mep.ductInsulation`) or its own into the model when its code is written, and draws a run by the model's thickness; it reads a `bti` of an older model as a code and a thickness, as rule 4 says, and writes none. Its schema holds the parts of a `bti` to the forms of the Swedish code; 0.1's take any string of 1 to 8 characters, as for every national value. It works a terminal's flow out from its own `deltaT`, else its heating system's in the project's `standards`, else that system's supply less its return; a terminal is on the system of the heating runs joined to it, else on the project's only heating system.

*Note (informative).* The reference implementation refuses a component in a pipe without a pipe for its host, and one in a duct whose `hostId` names anything but a duct (rule 6). It writes a component's place as this section says, its point and offset to 0.1 mm, and a component in a duct's size across to the whole mm, after every command (`followedRunComponents`); a command that moves a component's point slides it to the distance along its run of the point nearest its new point in plan. It does not refuse an `along` past its run's end: it keeps it, and stands the component at the end (rule 7; open questions, section 2, row 29). Its plans draw a component where its stored place is, and a component in a duct as large as its stored `width` and `height`, which it keeps where and as its run puts them; a reader of another writer's folder follows the run (rule 8). None of its types that mount on a pipe or a duct has connections (rule 9). Its library has twelve types for pipes, one of each kind and a water meter, and seven for ducts, two silencers, a fan, a fire damper, a damper, a smoke damper and a flow regulator, tagged by the codes of Swedish heating, plumbing and ventilation drawings, which are application values. A duct part it placed before its work 676 names no duct, and stays where it was put: it reads its system, its colour and its tag from the duct it stands on, the nearest within 300 mm in plan (`hostDuctAt`), until a writer hosts it.

### 6.10 Provisions for voids

*Part of 0.2. OpenEik 0.1 had it as the proposal `provision`: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), a reader keeps a record of the category `provision` as a later version's (Part I, 10, rule 5). The section adds a category, `provision`, and the member `provision` of its elements, and no file; [`schemas/0.2/elements/provision.schema.json`](https://openeik.org/schemas/0.2/elements/provision.schema.json) and [`schemas/0.2/types/provision.schema.json`](https://openeik.org/schemas/0.2/types/provision.schema.json) hold their schemas.*

By the steward's decision of 30 October 2026 (open questions, section 4, D9; question 85, point 4), from the reference implementation's requests for openings (its work 670, step 6.1). A run that crosses an element another discipline owns, a duct through a wall, a pipe through a slab, a cable tray through a beam, needs a void there, which the element's owner makes. A **provision** records that need as a record of its own: its asker places it without changing the element, and the element's owner answers it. IFC calls it a provision for void.

A provision is an element of the category `provision`. Besides the members every element has (section 6), it carries `provision`:

| Member | | Meaning |
|---|---|---|
| `hostId` | required | the element it goes through, its **host** (rule 3) |
| `axis` | required | `[x, y, z]`, a unit vector: the way it runs through its host |
| `up` | required | `[x, y, z]`, a unit vector square to `axis`: the way its `height` is measured |
| `depth` | required | more than 0, at most 20 000 mm: its length along `axis` |
| `round` | optional | `true` for a round void of diameter `width`; absent or `false`, a rectangle |
| `forIds` | optional | 1 to 20 element ids: the runs it is for (rule 4) |
| `clearance` | optional | 0 to 1000 mm: the space kept round its runs on each side, which its size includes |
| `note` | optional | at most 400 characters: what it is for, in words for people (`Supply air 400 × 200`) |
| `answer` | optional | its host's owner's answer (rule 5); absent, it waits for one |

`answer` is `{state, by, at, discipline?, text?, cut?}`:

| Member | | Meaning |
|---|---|---|
| `state` | required | `cut`, the void is made; or `declined`, it is not |
| `by` | required | 1 to 80 characters: who answered, as the project names them |
| `at` | required | when, an RFC 3339 date and time in UTC (`2026-10-30T09:15:00Z`) |
| `discipline` | optional | the discipline they answered for, one of the eight of Part XVI, 2, rule 4 |
| `text` | required where `state` is `declined` | 1 to 1000 characters: why, in a sentence; optional where it is `cut` |
| `cut` | required where `state` is `cut` | 1 to 20 entries, what the cut made: `{"recess": "<id>"}`, a recess of its host's `recesses` by its id, or `{"element": "<id>"}`, an element the cut made, such as an opening's own record; none where `state` is `declined` |

1. **Its place and its frame.** Its `profile` is one point, its centre in plan; its centre stands at its level's elevation plus its `offset` (Part I, 4). `axis` and `up` are unit vectors, their lengths within 10⁻⁶ of 1, and square to each other, their product within 10⁻⁶ of 0. With **across**, `up` × `axis`, they are its frame: across, up and axis as x, y and z, right-handed. A validator MUST report a provision whose `axis` or `up` is no unit vector, or whose `up` is not square to its `axis`, as an error (`provision-frame`).
2. **Its shape and its body.** A rectangular provision is `width` along across by `height` along up, centred on its centre; a round one is a circle of diameter `width`, and its `height` is not read. It runs `depth` along `axis`, half each way from its centre. Its **body** is that prism: a box, or a cylinder from its centre less `axis` × `depth`/2 to its centre plus `axis` × `depth`/2.
3. **Its host.** `hostId` names a wall, a slab, a foundation, a ceiling, a roof, a beam or a column. A validator MUST report a host of another category as an error (`provision-host`). A host that is not in the model is a reference that does not resolve (Part I, 7, rule 3), which a validator SHOULD warn of: the provision waits for its owner to remove it.
4. **Its asker.** `forIds` names the runs it is for, each a pipe, a duct or a cable carrier (Part XIII, 5). A validator MUST report an element of another category as an error (`provision-run`), and SHOULD warn of one that is not in the model. A provision is its asker's: in the owners' layout its owner is its `owner`, else the owner of the run its `forIds` names first, else `setup` (Part III, 4.4, rule 2). From 0.4, `forIds` may also name a terminal, or a component on a pipe or a duct, which section 6.10.1 says.
5. **Its answer** is its host's owner's to give, and nothing else of the provision is theirs. `cut` says that the host's owner made the void with the host's own records, and `answer.cut` names what the cut made: a recess of the host, or an element. `declined` says that they did not, and `text` says why. A writer gives an answer once, and changes or removes it only for the host's owner. A validator MUST report an answer `cut` without `cut`, and an answer `declined` without `text` or with `cut`, as errors (the schema states this), and SHOULD warn of an entry of `cut` that names a recess its host does not have, or an element that is not in the model (`provision-cut`).
6. **It cuts nothing.** A provision is no void of its host, answered or not: the host's body is what the host's own records make it (Part IV). Its body (rule 2) is what a view, a print or a check shows of the void asked for. It counts in no quantity, cuts no other body, and a clash check does not take it for a part of the building. A view MAY leave out a provision whose answer is `cut`, which what the cut made shows.
7. **In IFC**, a bridge writes a provision as Part VIII, 1.10 says: an IfcBuildingElementProxy of the predefined type PROVISIONFORVOID, its body rule 2's, with its size, its depth and its runs in IFC4's property set for a provision for a void.
8. **Commands.** A command processor makes and changes a provision and its type as any element and type (Part V, 1, rule 5), holding each to this section's schemas. It refuses a command that gives a provision a host of a category rule 3 does not list (`provision-host`), or a run of a category rule 4 does not list (`provision-run`). It keeps a provision whose host or run is not in the model, which the whole model's check warns of: `provision.hostId` is no `hostId`, so a `delete` of its host leaves the provision as it is (Part V, 5, rule 1), waiting for its owner to remove it (rule 3).

A duct's provision through an external wall, 450 mm wide and 250 mm high, its centre 1500 mm over level 0, asked by ventilation and not yet answered (compacted; on disk each member is a line):

```json
{
  "category": "provision", "height": 250, "id": "provision-a", "levelId": "level-0", "name": "Opening for supply air",
  "offset": 1500, "profile": [[2000, 0]], "revision": 0, "typeId": "type-provision", "width": 450,
  "provision": {"axis": [0, 1, 0], "clearance": 25, "depth": 220, "forIds": ["duct-supply-1"], "hostId": "wall-a",
                "note": "Supply air 400 × 200", "up": [0, 0, 1]}
}
```

Its host's owner cuts it as a recess of the wall, `wall-a`'s `recesses` entry `provision-a-cut`, and answers:

```json
{"answer": {"at": "2026-10-30T09:15:00Z", "by": "A. Architect", "cut": [{"recess": "provision-a-cut"}], "discipline": "architecture", "state": "cut"}}
```

*Note (informative).* What a path does not say: a provision is its asker's record, and its answer a field its host's owner writes. A host that enforces an owners file by paths (Part XVI) asks the provision's owner for a change to its answer, and does not see the field; an application that knows owners checks it, as Part III, 4.4's note says of fields that another discipline owns. A suggestion, a change offered to records that another party owns, is no provision: it is an envelope in the mode `propose` (Part XVI, 5, rule 5).

*Note (informative).* The reference implementation's request for an opening (its work 670, step 6.1, live since 26 October 2026) is this record, with these members, its type `provision-request` and the void it cuts a recess of its host, but in three points, which it changes (open questions, section 2): its answer's `state` is `accepted` where this text says `cut`; its answer's `cut` names one recess by its id, a string, where this text names what the cut made in a list (`[{"recess": …}]`); and its IFC print names the property set `Pset_ProvisionForVoid`, IFC 2x3's name, which IFC4 does not define, so that buildingSMART's rule PSE001 refuses it. It sizes a request by its run's outer size and 25 mm a side (`DEFAULT_CLEARANCE`), its depth by the run's span through the host and 10 mm past each face, and draws a round one with 32 sides, where this text's is a circle. A request whose host is gone waits for its owner to remove it.

Reference: `provisionSchema` and `provisionAnswerSchema` in `src/core/elements/provisionSchema.ts`; `provisionHosts`, `provisionRuns`, `validateProvision`, `provisionSolids`, `requestsFor`, `cutRecess`, `cutCommands` and `declineCommands` in `src/core/elements/provisions.ts`; `elementOwner` in `src/core/access/owners.ts` (its asker); `exportIfc` in `src/core/io/ifcExport.ts`. In `@openeik/tools`: `provisionChecks`, `provisionBody` and `provisionProperties` in `packages/openeik/src/provision.js`.

### 6.10.1 A provision for a terminal

*Part of 0.4. OpenEik 0.3 had it as the proposal `provisionTerminals` (open questions, 129): a model of 0.3, read by 0.3's rules (Part I, 9.6, rule 3), is read by it only where a caller asks for it (decision D12).*

A provision is asked by what passes through its host, a terminal or a fitting set in it as well as a run.

1. **What it is for.** `forIds` names the elements a provision is for, each a pipe, a duct or a cable carrier, as rule 4 of section 6.10 has it, or a terminal, or a component whose `hostId` names a pipe or a duct, such as a damper (section 6.9). A validator MUST report an element of another category, or a component on no pipe or duct, as an error (`provision-run`).
2. **Whose it is.** Its owner, where no `owner` is given, is the owner of the element `forIds` names first, as rule 4 says of a run (Part III, 4.4, rule 2), and its answer, and what its cut makes, are its host's owner's, as rule 5 says: the void is the host's owner's to make, whatever asks for it (open questions, 129, the steward's confirmation).
3. **In IFC**, as rule 7 says: a provision's system names in IFC4's property set for a provision for a void are the names of what its `forIds` names, a terminal's or a damper's among them.

Reference: none in the reference implementation, whose opening requests take a terminal as what they are for (its work 670). In `@openeik/tools`: `askerOf` and `provisionChecks` in `packages/openeik/src/provision.js`; the command processor's `checkProvisionRefs`; asked for with `proposals: ["provisionTerminals"]` on a model of 0.3.

## 7. References between elements

These element members name other elements. The list is the reference implementation's registry; a new member that names an element MUST be added to it.

| Path | Names |
|---|---|
| `hostId` | the host: a wall for an opening, a slab or a foundation for a slab edge element, a slab, a foundation, a beam, a column, a slab edge element or a wall for a rebar element, a slab, a foundation or a wall for a mesh element (section 6.1, rule 6), a stair for its shaft, a stair, a slab or a shaft for a railing (section 12.2), a pipe or a duct for a component in it (section 6.9), a gutter's roof, the gutter a downpipe was made from (Part V, 2.3) |
| `top.attach.hostId`, `top.cutRoofId` | the wall's attachment host and its cutting roof |
| `base.attach.hostId` | the column's base host |
| `edgeAttachments[].hostId` | a wall whose face trims an outline edge |
| `trussLayout.bearingWallIds[]` | bearing walls of a truss layout |
| `mep.joins.start.terminalId`, `mep.joins.end.terminalId` | the terminal a run's end joins |
| `joins[].to` | the member or plate a member end is joined to |
| `framingTop.floorId` | the floor a framing top follows |
| `bolts.through[]` | the elements a bolt group passes through and drills (section 6.3); of 0.2 |
| `castWith` | the element it is cast with in one pour (section 14.1); of 0.2 |
| `topRoofId`, `topCeilingId`, `roofCutId` | legacy (section 6.4) |

`hostId` of an opening, `top.attach.hostId`, `base.attach.hostId`, `edgeAttachments[].hostId`, `joins[].to`, and the hosts of slab edges, gutters, rebar and mesh MUST resolve: the reference validator refuses the model otherwise. So MUST a railing's `hostId` and each id in its `railing.edges`, which names an edge of its host (section 12.2, rule 3), a component's `hostId`, which names a pipe or a duct (section 6.9, rule 6), and a top constraint's level, `top.constraint.levelId` of a wall, a shaft or a stair: its height cannot be worked out without it (Part IV, 5.1). The others SHOULD resolve. In 0.2, each id of a bolt group's `bolts.through` MUST resolve too, as the reference validator refuses a group whose `through` names no element (section 6.3, the note), and an element's `castWith` SHOULD.

Reference: `ELEMENT_REFERENCES` in `src/core/model/copies.ts`; `validateElement`, `findAttachHost` (`wallTop.ts`), `findBaseHost` (`columnBase.ts`), `validateMemberJoins`.

## 8. Profiles

A profile groups the members and payloads of one domain, so that a reader can say which domains it understands.

| Profile | Members (elements) | Payloads (types) |
|---|---|---|
| core | the members of section 6, `hostId`, `along`, `voids`, `top`, `edgeAttachments`, `edgeIds`, `status`, `original`, `flipped`, `handFlipped`, `roomBounding`, `wallOutline`, `recesses`, `roof`, `joinEnds`, `declaredGeometry`, `generic` | `layers`, `section`, `windowFamily`, `openingFamily`, `outline`, `footing`, `wallAttach`, `declaredGeometry`, `generic` |
| structure and fabrication | `endRise`, `turn`, `roll`, `taper`, `base`, `connection`, `joins`, `holes`, `upright`, `axes`, `plate`, `bolts`, `weld`, `jointId`, `insertAt`, `edges` | `component`, `insert`, `edgeDetail`, `edgeProfile`; pieces and joints |
| timber framing | `framingParameters`, `framingTop`, `framingJoints`, `framingCorners`, `framingStart`, `framingOffset`, `framingStuds`, `framingTees`, `bottomPlate`, `frameHost`, `trussLayout` | `framing`, the layers' `framing` and `joists` |
| reinforcement | `reinforcement`, `bars`, `sheet` | |
| systems | `mep`, `terminal` | `pipe`, `duct`, `terminal` |
| site | `terrain`, `grading`, `propertyLine`, `road`, `parking`, `planting` | `road`, `parking`, `planting` |
| interiors | `room`, `kitchen` | `room`, `furniture` |
| circulation | `stair`, `railing` | `stair`, `railing`, `shaft` |
| fire | `fire`, `fireClass` | `fire`, `fireClass` |
| documentation | views, detail items, scope boxes, dimension styles, detail families | |

1. A reader that does not implement a profile MUST keep that profile's members and records unchanged when it writes the model back.
2. Profile members are flat members of the element, as the reference implementation stores them, and they stay flat (decided 11 October 2026). A model does not declare its profiles yet.
3. **No national code lists.** OpenEik carries no national classification or code list (BSAB, CoClass, AMA, SS 21054 or any other) and defines no regional profiles. A field whose values come from such a list in practice is a plain string: a room type's area class (`room.ssClass`), a view's area plan (`display.areaPlan`), the shape codes and grades of bars and cast-in bars, the designations and grades of mesh sheets, the precast reinforcement grades and meshes of the project, and the insulation codes of pipes and ducts and their parts (section 6.9). The schemas accept any non-empty string there and mark the field `"x-eik-values": "national"`. A building-part catalogue (a type's `assembly`) is an application value (section 9.2).
4. A reader MUST keep these values unchanged and MUST NOT need a code list to read them. A validator MUST NOT refuse a value because a code list does not contain it.

Why no regional profiles: a code list belongs to its publisher, who sets its licence and its editions. A standard that listed codes would have to license them, follow their editions and choose which countries' lists to carry, and an implementation would need them to conform. Leaving the values to their publishers keeps OpenEik usable with any system, in any country. Classification references (section 11) let a record name a code of any system without the standard licensing or listing it.

*Note (informative).* The annotation means only that OpenEik defines no values for the field. Until 11 October 2026 the schema generator wrote it as `"x-eik-profile": "regional"`, from the time regional profiles were planned; it was renamed when they were dropped (open question 16).

## 9. Application records and values

### 9.1 Application records

Title blocks, images, reports, renders, deviations, area adjustments and linked references are records of the application that wrote the model. OpenEik carries them but does not define them.

In 0.1, drawing production stays with the application: sheets, title blocks and images remain application records and do not join the documentation profile (decided 11 October 2026). In 0.2, sheets and issued drawings join the standard (the steward's decision of 15 October 2026): a sheet is a record of [Part XIV, 2](https://openeik.org/spec/0.4/drawings#2), and a sheet of 0.1 that is not one stays an application record (Part XIV, 2.1, rule 1; Part I, 9.8, rule 5). Title blocks and images stay application records.

1. Each is a JSON object with an `id`; `application-record.schema.json` checks only that.
2. A reader MUST keep application records unchanged when it writes the model back, and MUST NOT require them.
3. A writer MAY omit them.

### 9.2 Application values

Some members of defined records hold application values: a type's `assembly` (a building-part catalogue), `catalogSource` and `handbook`, the project's `standards`, the font choices of views and dimension styles, a symbol's name and a port's, and the rows of a tag template (section 13). The schemas mark them `"x-eik-application": true` and accept any value of the right JSON type. A reader MUST keep them unchanged. They are the application's, not the standard's: OpenEik does not define a catalogue, a handbook, a drawing standard or a font (decided 11 October 2026). An application's own data beside them goes in extension members and in its own folder (Part I, 11).

## 10. Derived data is not a record

Bodies, meshes, joins found from geometry, room boundaries and areas, quantities, framing members derived from a rule, stair runs, fittings and the elements of placed copies are derived (Part IV). They MUST NOT be stored as records. Two kinds of record come from derived data because a person or a command made them so:

- **materialised members**: framing members or trusses written as elements by a command (`framing.materialise`, `trusses.materialise`) carry `frameHost` and are ordinary elements from then on;
- **originals and copies**: a placed group is stored as its original's elements plus a `copy` record; the elements of every instance but the one it keeps are derived, named as section 10.1 says, `<copy id>/<base id>` for a copy, and are never stored.

A view is a record (its settings and its 2D items); the drawing it shows is derived.

Reference: `storedOf` and `workedOut` in `src/core/model/copies.ts`; `migrateManifest` drops stored copy elements on load.

### 10.1 A group's kept instance and its instances' ids

*Part of 0.3. OpenEik 0.2 had it as the proposal `groupIds` (open questions, 126): a model of 0.2, read by 0.2's rules (Part I, 9.6, rule 3), is read by it only where a caller asks for it (decision D12).*

With Part I, 6.1. A **group** is an original (`project/originals/<id>.json`) and its **kept elements**, the elements whose `original` names it, placed once for each of its **instances**: its first instance, whose id is the original's own, and each copy (`project/copies/<id>.json`) whose `of` names it. These rules say which instance's elements the kept ones are, and the id each element has in each instance. In a model of 0.2, a validator applies it only where the proposal is asked for.

1. **The kept instance.** A group keeps its elements once, as the elements of one of its instances, where that instance stands: the original's `frame`, the id of that instance, or, without `frame` or where it is the original's own id, its first instance. The original's `origin`, `turned` and `lifted` say where the kept elements stand. A `frame` that is not the original's own id MUST name a copy whose `of` names that original and which stands there: its `at` the original's `origin`, its `turn` the original's `turned` and its `lift` its `lifted`, each 0 where it is absent, and no `levelId`. The first instance has a copy record of its own, whose id and `of` are the original's id, only while it is not the kept instance; while it is, it has none. A validator MUST report a `frame` that breaks this rule, and a first instance's copy record while its elements are the kept ones (`group-frame`).
2. **An element's base id.** Each kept element was drawn in one instance: its `born`, or, without it, the first instance. Its id in that instance is its **base id**, and in any other instance its id is that instance's id, a `/` and its base id. Its stored id is its id in the kept instance: its base id where it was drawn there, else the frame, a `/` and its base id. A validator MUST report a kept element drawn in another instance than the kept one whose id does not begin with the frame and a `/` (`group-id`).
3. **The instances' elements.** Every instance but the kept one derives its elements from the kept ones (section 10), each under its id in that instance (rule 2), and none is stored: a copy's are `<copy id>/<base id>`, but for those drawn in it, which have their base ids; and the first instance's, where it is not the kept one, have their base ids, but for those drawn in another instance. The kept instance derives none: its elements are the stored ones.
4. **No stored id is a derived one.** No stored element has an id an instance derives (rule 3). A validator MUST report a stored element whose id one does (`id-derived`), naming the instance and the group; where that id begins with the instance's id and a `/`, Part I, 6.1, rule 2 reports it already (`id-slash`).

Reference: `frameOf`, `bornIn`, `baseId`, `idIn` and `instancesOf` in `src/core/model/groups.ts`; `workedOut` and `validateCopies` in `src/core/model/copies.ts`. In `@openeik/tools`: `groupRules` in `packages/openeik/src/check.js`, for a model of 0.3, and for one of 0.2 asked for with `proposals: ["groupIds"]`. It derives no instance's elements: a compiler of 0.2 does not expand copies (Part IV, 1).

*Note (informative).* Which instance is the kept one changes when a group is edited at another of its instances (`original.frame`): nothing moves then, and no element's id changes, because an id belongs to an instance and not to the keeping. So the first instance keeps the plain ids its elements had when the group was made, and an element drawn while the group is edited at a copy has there the id it was given.

## 11. Classification references

*Part of 0.2. OpenEik 0.1 had it as the proposal `classifications`, and this section was informative: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), `classifications` is a member a schema of 0.1 does not know, which a reader keeps (Part I, 10). The section adds this one member and no file.*

By the steward's decisions of 11 October 2026 (open questions, 4) and 27 October 2026 (open questions, 86, "C ja"): a record names codes of classification systems that OpenEik does not list, so that any system can be used without the standard licensing or listing it.

```json
{
  "classifications": [
    {
      "code": "<code>",
      "edition": "<edition, optional>",
      "system": "<name or URI of the system>",
      "uri": "<the code's URI, optional>"
    }
  ]
}
```

1. **Which records.** Any record MAY carry `classifications`, a list of 1 to 100 references, at its top: the project, in its `project`; a level; and each record of the record lists (Part III, 9), whatever its kind: a grid, a material, a type, an element, an original, a copy, a view and the rest. The order, which is no record, and a view's detail items, which are parts of their view, carry none.
2. **A reference** is `{system, code, edition?, uri?}`. `system` names the classification system, by its name or a URI, 1 to 200 characters; `code` is a code of that system, 1 to 100 characters; `edition` names the system's edition, 1 to 100 characters, where it matters which; and `uri` names the code itself, by the URI its publisher gives it, such as its URI in buildingSMART's Data Dictionary (bSDD): an absolute URI (RFC 3986), at most 2000 characters. A writer SHOULD give `uri` where the publisher gives one, so that a reference names its code the way buildingSMART's IFCX names a node's class, by a bSDD URI. A member of a reference that this section does not define is one a reader keeps, as in any record (Part I, 10).
3. **Several references.** A record MAY carry several references, of one system or of several. It MUST NOT carry one reference twice. The order of the list has no meaning.
4. **No list of systems or codes.** OpenEik does not list systems or codes, and a validator does not check that a code is one of its system's: the system's publisher defines its codes and their licence, and the writer of the record says which system it uses. A validator MUST NOT refuse a reference because it does not know its system or its code.
5. **Beside, never in place of.** The plain strings and application values of sections 8 and 9.2 stay as they are: a classification reference is added beside them, never in their place, and a reader reads neither from the other.
6. **A bridge** that writes IFC writes each reference of a record it prints an object for as an IfcClassificationReference of its system (Part VIII, 1.8), its `Identification` the code and its `Location` the `uri`, and says in its fidelity report which references it does not print.

The class 21.21 of NL-SfB, as buildingSMART's IFCX examples classify a wall: `{"system": "NL-SfB", "edition": "2005", "code": "21.21", "uri": "https://identifier.buildingsmart.org/uri/nlsfb/nlsfb2005/2.2/class/21.21"}`. A wall may carry it beside a code of another system, `{"system": "Uniclass", "edition": "2015", "code": "EF_25_10"}`, which gives no URI here.

*Note (informative).* The reference implementation keeps no `classifications` and writes none yet.

### 11.1 An estimate's members

*Part of 0.3. OpenEik 0.2 had it as the proposal `estimate` (open questions, 116): a model of 0.2, read by 0.2's rules (Part I, 9.6, rule 3), is read by it only where a caller asks for it (decision D12).*

The records say what an estimate sorts and prices a building by: the mark an element is called by, the tender part it is bought in and what is done to it, and the codes that cost books price a type and a room's finishes by. A code is an identifier only: a model holds no price and no cost book, and OpenEik lists no cost book's system or codes (section 11, rule 4). In a model of 0.2 that does not ask for the proposal, these members are members its schemas do not know, which a reader keeps and a validator warns of (Part I, 10).

1. **A mark.** An element and a type MAY carry `littera`, the mark an element is called by on drawings, in schedules and in an estimate: a type's, such as `D1`, `F3` or `IV2`, for every element of it, and an element's own, over its type's. It is 1 to 20 characters, with no white space at its start or its end. The mark an element is called by is its own `littera`, else its type's, else none.
2. **A tender part and an action.** An element MAY carry `tenderPart`, the part of a tender it is bought in, such as `Frame` or `Stage 2`, 1 to 40 characters, and `action`, what is done to it where its `status` (`existing`, `new` or `demolished`) says too little, such as `Replace` or `Remove and refit`, 1 to 60 characters; each with no white space at its start or its end. Their values are the writer's words: OpenEik lists none, and a reader compares them as they are written.
3. **A cost code.** A code that a cost book prices a record by is a classification reference (section 11) of the cost book's system, in the record's `classifications`: a type's above all, or a room's finish's (rule 4). Beside the members of section 11, rule 2, such a reference MAY carry:
   - `measure`, the quantity the code is priced by: `area`, `areaGross` (the area before its openings are taken out), `length`, `count`, `volume` or `mass`; absent, the quantity the reader prices the record's category by;
   - `size`, the size the code is for, as the drawings write it, such as `110` or `400×200`, 1 to 20 characters: a code of a pipe's, a duct's or a cable carrier's type for that size alone, where a code without one is for every size that has none;
   - on a pipe's or a duct's type, `item`, what of its runs the code prices besides the runs themselves: `bend`, `branch`, `reduction`, `transition` or `insulation`, where a code without one prices the runs; `angle`, a bend's angle in degrees, more than 0 and at most 180, on a code whose `item` is `bend`; `insulation`, the insulation code (section 6.9) of the runs whose own insulation the code prices, 1 to 12 characters, on a code whose `item` is `insulation`; and `includes`, what the runs' own code prices with their length, `fittings`, `insulation` or both, on a code with no `item`.
   A validator reports an `angle` on a code whose `item` is not `bend`, an `insulation` on one whose `item` is not `insulation`, and `includes` on one with an `item` (`cost-code-member`). A reference with none of these members is a classification reference as any other.
4. **A room's finishes.** A room, and a type of rooms, MAY carry `classifications` in each of its finishes, `room.finishes.floor`, `skirting`, `wall`, `ceiling` and `other`: the codes the finish is priced by, as rule 3 says, and any other classification of its work, as section 11 says.
5. **Its print.** A bridge that writes IFC writes a type's `littera` as the property `Reference` of the property set IFC4 defines for its class, the set `Pset_` … `Common` that IFC4 applies to the type object and that defines `Reference` (Part VIII, 1.5, rule 2), and an element's own `littera` so on its object, where IFC4 applies such a set to it. It writes a mark that no such set takes, and an element's `tenderPart` and `action`, in a property set of its own, `OpenEik_Estimate`, as `Littera`, `TenderPart` and `Action`. It writes a cost code as any classification reference (section 11, rule 6): IFC has no place for a reference's `measure`, `size`, `item`, `angle`, `insulation` or `includes`, which it does not write.

Reference: `elementEstimateShape`, `typeEstimateShape`, `costCodeSchema` and `litteraOf` in `src/core/model/estimateFields.ts` (Stomra works 758, 768 and 772); `finishSchema` in `src/core/elements/roomTypes.ts`. In `@openeik/tools`: `estimateKnown` and `estimateChecks` in `packages/openeik/src/estimate.js`; for a model of 0.3 the schemas of 0.3, and for one of 0.2 asked for with `proposals: ["estimate"]` the proposal's index, which `proposed03Members` in `packages/openeik/src/check.js` holds; and `markSetOf` in `packages/openeik/src/ifc.js`.

*Note (informative).* The reference implementation writes these members without a prefix, which 0.2 does not define (open questions, section 2, row 20): `littera`, `tenderPart` and `action` as this section has them, and a type's and a room's finish's `costCodes`, `{system, code, edition?, measure?, size?, item?, angle?, insulation?, includes?, proposed?, note?}`. Under the proposal its codes are classification references, each the same object in the record's `classifications`, its `proposed` (set on a code that someone suggested and no person has checked) and its `note` written as `stomra:proposed` and `stomra:note` (Part I, 11), which only its interface reads. It keeps at most one code for each system, size, item, angle and insulation, and a reader that prices a quantity takes the most particular code of its system that fits it: the one for its size, then for its angle, then for its insulation. The steward decided the members on 30 October 2026 (open questions, 116); the reference implementation added `item`, `angle`, `insulation` and `includes` after the question was raised (its work 772), and they follow the same rule, question 115's: an estimate is read by tools other than the one that wrote it.

## 12. A supplier's parts

The records of this section serve the supplier proposal flow, which the steward approved on 11 October 2026: a supplier's declared geometry (12.1) and railings for product systems (12.2). Their principles were decided on 11 October 2026 and proposed for 0.2; on 14 October 2026 they were adopted into 0.1 (open questions 17 and 18). `types/type.schema.json` and `elements/element.schema.json` define `declaredGeometry` and the members of `railing`, and the category schemas add the rules of each category.

In the supplier proposal flow, the supplier of a building part (railings first, then any part) is invited to a project. It reads a named version, finds its parts by category, and checks them with its own tool. It proposes changed records as typed commands (Part V), with a quote. The client merges one proposal. The quote lives with the proposal on the host: it is not part of the model.

### 12.1 Declared geometry: a part at a declared level of detail

**The principle.** A supplier's records can carry geometry at any level of detail, the supplier's choice. It ranges from the **interface only** up to the **full manufacturing model**:

- **The interface** is what other trades and checks meet: the exact outer envelope, the fixing and connection points, clearances, positions such as posts and fixings, the loads the part puts on the structure, and the declared tolerances. It is enough for exact geometric checks and layout control, and it is a valid minimum, not the rule.
- **The implementation** is the supplier's own to keep back or to share: internal profiles, joints, machining and cutting data, article numbers. A supplier that shares none of it publishes the interface, not the implementation; one that shares all of it publishes its manufacturing model.
- **Costs, prices and quotes** are never part of a record. In the supplier flow the quote lives with the proposal on the host.

A record says how far it goes: it declares its **level** and what its geometry is **authoritative for**. As in a code base, a library may ship only its interface, its declarations, and keep its implementation: a compiler checks every use against the interface exactly and assumes nothing about the code behind it.

**Where it lives.** A type or an element MAY carry `declaredGeometry`:

- **On a type**, for geometry that is the same for every element of the type: a window, a door, a component, a railing's section. Its coordinates are in the type's frame:
  - a category placed at a point (`component`, `furniture`, `terminal`, `column`, `generic`): the element's insertion point at its base, x along its orientation, z up; a component with `axes`, the frame they give (section 6.3);
  - a hosted opening (`door`, `window`): x along the host wall from the opening's near edge, y across the wall from its centreline towards the wall's exterior side (Part I, 5.1), z up from the opening's bottom;
  - a category placed along a line (`railing`, `beam`, `slabEdge`, `gutter`): a section square to the line (x across it, z up from the line's base), swept along the whole line.
- **On an element**, for geometry that depends on where the element is: the posts and fixings of a railing along given edges, a stair kit in a given shaft. Its plan coordinates are those of the element's `profile` (the project's, in mm); its heights are measured from the element's base, `elev(levelId) + offset` (Part I, 3.3 and 4).

For each aspect, an element's declaration wins over its type's; the aspects an element does not declare come from its type's.

A type of a category that this list does not name (a stair's, a slab's, a wall's …) has no frame in 0.1. Its declaration is kept and checked as any declaration is, but placed nowhere: a compiler derives nothing from it (Part IV, 11.1), and a check cannot use it. Such a part declares its geometry on its element, in the `world` frame (rule 7). From 0.2 a stair's type and a wall's type have a frame (section 12.1.1).

A window type declaring its interface, in its own frame (compacted; on disk each member is a line):

```json
{
  "declaredGeometry": {
    "authoritativeFor": ["envelope", "fixings", "clearances"],
    "clearances": [
      {"body": {"bottom": 0, "kind": "prism", "profile": [[0, -45], [1200, -45], [1200, -650], [0, -650]], "top": 1200}, "id": "sash-swing", "purpose": "operation"}
    ],
    "envelope": [
      {"bottom": 0, "kind": "prism", "profile": [[0, -45], [1200, -45], [1200, 45], [0, 45]], "top": 1200}
    ],
    "fixings": [
      {"at": [0, 0, 200], "bearsOn": "reveal", "direction": [-1, 0, 0], "id": "fx-1"},
      {"at": [0, 0, 1000], "bearsOn": "reveal", "direction": [-1, 0, 0], "id": "fx-2"},
      {"at": [1200, 0, 200], "bearsOn": "reveal", "direction": [1, 0, 0], "id": "fx-3"},
      {"at": [1200, 0, 1000], "bearsOn": "reveal", "direction": [1, 0, 0], "id": "fx-4"}
    ],
    "level": "interface",
    "tolerances": {"default": 2, "fixings": 1}
  }
}
```

The window's profiles, its sash and its glazing are not declared: a check knows the space the window takes, where it is fixed and what must stay free in front of it, and nothing about what is inside.

**The levels.** `level` is one of three, in order:

| Level | The geometry is | It MAY be authoritative for |
|---|---|---|
| `interface` | the part as other trades meet it: the space it takes, where it is fixed, what must stay free, and the positions and loads that matter to others | `envelope`, `fixings`, `clearances`, `positions`, `loads` |
| `assembly` | the interface, and each separate part as installed (posts, rails, panels, brackets, a sash), with its outer body and its place | the above, and `parts` |
| `fabrication` | the full manufacturing model | the above, and `fabrication` |

**What it is authoritative for.** `authoritativeFor` lists the aspects the geometry is exact for, from this closed vocabulary:

| Term | The declaration states, exactly within its tolerance |
|---|---|
| `envelope` | the outer envelope: the whole space the part takes as installed, as bodies |
| `fixings` | the fixing and connection points: where the part is fixed to another record or connects with it, in which direction, and which face it bears on |
| `clearances` | the zones that must stay free: to install, operate (a sash's or a leaf's swing), maintain or replace the part |
| `positions` | the set-out points or lines of the part's distinguishable members (posts, brackets, mullions, hinges), without their shapes |
| `loads` | the loads the part puts on the structure at each fixing, per named load case, as characteristic forces in kN and moments in kN·m in the model's axes |
| `parts` | the separate parts as installed, each with an id, a name and its outer body |
| `fabrication` | fabrication data: internal profiles, joints, machining, cutting and assembly data. OpenEik does not define its form: it is the supplier's application value (section 9.2), or the supplier's kept files (Part III, 7) named by path and hash |

**Rules:**

1. `level` and `authoritativeFor` are required. `authoritativeFor` MUST contain `envelope`, MUST name each term once, and MUST name only terms its level allows. `envelope` with any of `fixings`, `clearances`, `positions` and `loads` is a complete declaration at the `interface` level.
2. A declaration MUST carry data for every term it lists, and MUST NOT carry data for a term it does not list.
3. `tolerances` gives, in mm, how far the built part may lie from what is declared: a `default`, and a value per term where it differs; it has no other members. Every geometric term listed (`envelope`, `fixings`, `clearances`, `positions`, `parts`) MUST have a tolerance, its own or the default.
4. Bodies are of the body kinds of Part IV, 8, written with these `kind` names: `prism` `{profile, bottom, top}`; `polyhedron` `{vertices, faces}` (convex); `mesh` `{vertices, faces}` (closed); `cylinder` `{centre, radius, bottom, top}`; `rod` `{from, to, diameter}`; and, on a type placed along a line, `swept` `{section: [[x, z], …]}`, with x across the line towards the walking side, swept piece by piece along each segment. In the `line` frame a body is mapped point by point, so it bends only at its vertices: a line-frame body SHOULD stay within one segment of the line.
   The other records: a clearance is `{id, purpose, body}`; a position is `{id, member?, at, to?}`, with `to` for a position along a line; a load is `{fixing, case, force, moment?}`, in kN and kNm, acting at the declared fixing whose `id` its `fixing` names; a part is `{id, name, bodies}`. A writer that mirrors a declaration (rule 6) MUST reflect a load's force, and reflect its moment and then change its sign, since a moment is an axial vector: with the reflection R, F' = R·F and M' = −R·M (mirroring x → −x turns M (4, 5, 6) into (4, −5, −6)).
5. An element's declaration SHOULD name its `basis`: the id and the hash (Part III, 6: SHA-256 of the record's canonical JSON, the function the version tree uses) of each record it was made against, such as the host and the edges it follows. A reader reports a basis whose hash has changed, keeps the declaration, and treats it as unconfirmed.
6. A writer that moves, turns, mirrors or copies an element (the transforms of Part V, 2.3) MUST move, turn, mirror or copy its declared geometry with it.
7. **The frame of an element's declaration.** `frame` is `world` (absent: the project's plan coordinates, as above) or, for a category placed along a line, `line`: a point is `[s, w, z]`, with `s` the distance in plan along the element's line from its first point, `w` the distance across it towards the walking side, and `z` the height over the element's base. A declaration in the `line` frame follows its host's line the way `railing.posts.at` does.
8. **A fixing** gives its `id`, its point `at` and its `direction` (a unit vector), and SHOULD say what it `bearsOn` (`reveal`, `edgeFace`, `slabTop` and so on). It MAY give its `kind` (`bolt`, `anchor`, `bracket`, `screw` …) and its `size` in mm.
9. **Who declared it.** A declaration MAY name its `source`: who made it and with what, as `{"by": …, "tool": …, "version": …}`. It says nothing about responsibility; a host's signed merge records that.

The schemas state rules 1 to 3 and the shapes of rules 4, 5 and 7 to 9: `fabrication` is marked as the supplier's application value, a `swept` body is allowed only in a type of a category placed along a line, and the `line` frame only on an element of such a category. A validator checks the rest in code, such as that a load acts at one of the declaration's fixings.

Reference: `declaredGeometrySchema` in `src/core/model/declaredGeometry.ts` (rules 1 to 3, the shapes of rule 4, rules 5 and 7 to 9); `mappedDeclaration` there, called by the transforms (rule 6); `unconfirmedBasis` (rule 5).

**How readers, checks and compilers treat it.**

1. **Exact for what it declares, silent on the rest.** A reader, a check or a compiler MUST use declared geometry only for the aspects its `authoritativeFor` lists, and MUST NOT infer from it anything about the others: an envelope says nothing about the parts inside it.
2. **Readers** keep it unchanged (Part I, 10). A viewer MAY draw the declared envelope, or the declared parts, in place of the body a compiler derives from the type's payload, and SHOULD say that it shows a supplier's declared geometry, at which level.
3. **Checks use what is declared.** A clash check uses the declared envelope, grown by its tolerance, in place of the derived body, and treats each clearance, grown by its tolerance, as space no other body may enter. A layout check uses the declared positions and fixings: a fixing MUST lie on the face it names, within its tolerance. For an aspect that is not declared, a check uses the derived body and says so in its result. A check that needs an aspect that is not declared (the gaps between balusters need `parts`) MUST report that the declaration cannot decide it: it neither passes nor fails it.
4. **Compilers derive nothing beyond what a record declares itself authoritative for** (Part IV, 11.1).
5. **A changed basis.** When a record named in `basis` has changed since (its hash differs), the declaration was made for another version of that record. Readers keep it and checks still use it, but they report it as unconfirmed until the supplier confirms or replaces it. It is never discarded.

**Declared, not derived.** Section 10 and Part IV, 1, keep derived data out of records. Declared geometry is not derived data:

| | Derived data (Part IV, 1) | Declared geometry |
|---|---|---|
| Made by | a compiler, from the model's records, by the rules of this specification or a reference rule | the supplier, from its own product and its own rules, which are not in the model |
| Made again from the records? | yes: deleting it loses nothing | no: deleting it loses the supplier's statement |
| Where it lives | never in a record; at most in `.eik/derived/`, keyed by the hashes of its sources (Part IV, 1.1) | in the record, a type or an element, written by a command and merged by a person like any other change |
| When its sources change | discarded and derived again | kept, and reported as unconfirmed (rule 5 above) until the supplier confirms or replaces it |
| Authority | the rule that made it | exact for what it declares, within its tolerances; silent on the rest |

A declaration is source, as a door type's sizes are (section 5.3): what a supplier states about its product. Nor is it a materialised member (section 10): those are written from the model's own derived data, and the model's rules could make them again; nothing in the model can make a declaration again. What a compiler works out from a declaration (its bounds, a section through its envelope) is derived as before, and is not stored.

*Note (informative).* Two questions about declared geometry were deferred to 0.2 by the steward on 15 October 2026 (open questions, 17): the frame of a declaration on a type of a category that "Where it lives" does not name, and whether the sizes a type sets (section 5.3) must agree with its declared envelope. Meanwhile such a declaration is placed nowhere (above), and nothing requires the sizes to agree with the envelope: the sizes size the element and the body a compiler derives, and where a body stands for the space the part takes, the declared envelope replaces it (Part IV, 11.1, rule 2). On 21 October 2026 the steward decided what 0.2 says: the sizes need not agree, staying the nominal sizes a schedule shows, and a validator MAY warn where they differ by more than the declaration's tolerance; and a stair's type and a wall's type get a frame, a stair kit's and a prefabricated wall's (open questions, 17). Section 12.1.1 gives it.

*Note (informative).* The reference implementation reads, checks and keeps declared geometry on any element or type since Stomra pull request #787 (live since 12 October 2026, `main` `1e1089db`), and its commands accept it. It draws a declared envelope, or declared parts, in place of the derived body; moves, turns and mirrors a world-frame declaration with its element and has a line-frame declaration follow its line; and reports a changed basis. It does not check the restrictions of `swept` bodies and of the `line` frame to categories placed along a line. Its other gaps are in [Conformance, 3](https://openeik.org/spec/0.4/conformance#3).

### 12.1.1 Nominal sizes, and a stair kit's and a prefabricated wall's frame

*Part of 0.2. OpenEik 0.1 had it as the proposal `declared`: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), a declaration on a stair's or a wall's type is placed nowhere, as section 12.1 says. The section adds no file and no member.*

By the steward's decision of 21 October 2026 (open questions, 17), on the two questions deferred on 15 October 2026. Suppliers ship a stair kit and a prefabricated wall whole, and declare them on their type, as they declare a window.

1. **Nominal sizes.** The sizes a type sets (section 5.3) need not agree with its declared envelope. They stay its nominal sizes, which schedules and tags show, and the declared envelope is the part's geometry where a body stands for the space it takes (Part IV, 11.1, rule 2). A validator MAY warn where a size differs from the envelope's extent in the type's frame by more than the declaration's tolerance for `envelope`, its own or its `default` (`declared-size`): `width` from the extent along the frame's x, `height` from the extent along its z.
2. **A stair's type**, a stair kit's: its declaration's frame has its origin at the stair's start, its profile's first point, at its base, `elev(levelId) + offset`; x runs up its first run, towards the profile's second point in plan; y to its left, looking up the stair; z up. It is the stair's frame of Part IV, 7.3.
3. **A wall's type**, a prefabricated wall's: the origin at the baseline's first point, at the wall's base, `elev(levelId) + offset`; x along the baseline; y to its left, the wall's exterior (Part I, 5.1); z up. The wall's top, attached or of its own outline (Part IV, 5.3), moves nothing in the frame.
4. **Other categories.** A type of a category that neither section 12.1 nor rules 2 and 3 give a frame keeps none: its parts declare their geometry on their element, in the `world` frame (section 12.1, rule 7).
5. **A compiler** places a stair's and a wall's type's declaration in these frames, as section 12.1 places the others' (Part IV, 11.1): its envelope stands where the element's body stands for the space it takes.

A prefabricated wall element 2400 mm long, 3000 mm high and 210 mm thick declares on its type the envelope `{"bottom": 0, "kind": "prism", "profile": [[0, -105], [2400, -105], [2400, 105], [0, 105]], "top": 3000}`, centred on the baseline as a wall's body is (Part IV, 4.1), its fixings at its foot, `[600, 0, 0]` and `[1800, 0, 0]`, and its tolerances `{"default": 3}`. Each wall of the type places it at its own baseline's first point, along its baseline, its exterior face at y = 105.

*Note (informative).* The reference implementation draws a type's declaration only for railings (open questions, section 2, row 15), and so places neither a stair kit's nor a prefabricated wall's yet.

### 12.2 Railings for product systems

A railing supplier's tool details railings on the edges of slabs and makes them. Without the members of this section, a railing's records are too thin for a product system, and two implementations place the posts of one railing differently. In a study of 11 October 2026, a railing on three edges of a balcony slab, written with the reference implementation's commands, got eight posts from the reference implementation and eight from a railing supplier's tool: at the corners in one, 200 mm from them in the other. This section adds what a product system needs.

**The members.** An element's `railing` is `{side?, edges?, inset?}` and the members added below. A type's `railing` has thirteen members, `kind` (`balusters`, `glass`, `panel` or `handrail`), `height`, `postSpacing`, `post`, `railWidth`, `railDepth`, `baluster`, `gap`, `clearance`, `panel`, `handrail`, `handrailDiameter` and `material`, each required by the kinds that use it (the table below), and the members added below. A railing's line, posts and infill are reference behaviour (Part IV, 7.2), except the line of a railing on an outline and its given posts (Part IV, 11.2).

**Host and edge rules** (section 6.1, rule 11, and section 7). The reference implementation checks them.

1. A railing's type MUST have a `railing` payload.
2. A railing stands on a stair, on the outline of a slab or a shaft, on a wall's top, along a slab edge element, or on a line of its own:
   - on a stair: `hostId` names the stair, and `railing.side` (`left` or `right`, looking up the stair) MUST be given;
   - on an outline: `hostId` names a slab or a shaft, and `railing.edges` MUST be given: ids of the host's edges (its `edgeIds`, or `<host id>:edge:<n>` where it has none; Part I, 6.6) that follow one another round the outline as one run. Naming every edge makes a closed run;
   - on a wall's top or along a slab edge element: `hostId` names the wall or the element, and `railing.side`, `railing.edges` and `railing.insets` MUST NOT be given (below);
   - with no host: its `profile`, two points or more, is its line, and its base is `elev(levelId) + offset`.
3. A railing's `hostId` (a stair, a slab, a shaft, a wall or a slab edge element) and each id in `railing.edges` (an edge of its host) join the references that MUST resolve (section 7).
4. A railing on an outline stands on its host's top along its line (Part IV, 11.2); a railing on a stair is measured from the stair's nosing line (reference behaviour). A hosted railing's `profile`, `levelId` and `offset` are written from its host: a writer rewrites them in the same command whenever the host changes. The profile is the railing's line in plan, the level is the host's (for a shaft with a top constraint, that constraint's level), and the offset is 0. The host and the edges are the intent; the profile is the writer's copy of the line. A reader MAY use it for a railing on an outline, MUST follow the host where the two disagree, and gets no heights from it for a railing on a stair. From 0.2 the copy of a railing on an outline is deprecated, and such a railing may leave it out (section 12.2.1).
5. `railing.side` has a meaning only on a stair, and `railing.edges` and `railing.insets` only on an outline.

Reference: `validateElement` in `src/core/model/model.ts` ("A railing needs a railing type.", "A railing on a stair names its side: left or right.", "A railing on an outline names the edges it runs along."); `railingLine` and `chain` in `src/core/elements/railings.ts`; `followedRailing` in `src/core/elements/hostedFollow.ts`.

**The members added for product systems**, all optional, so that a railing written without them stays valid and keeps its shape. On the type's `railing`:

| Member | Meaning |
|---|---|
| `mount` | `top`: the posts stand on the host's top (the default, and the behaviour of a railing without this member). `side`: the posts are fixed to the host's edge face and reach `belowBase` below its top |
| `postWidth`, `postDepth` | a rectangular post: its size along the line and across it, in mm. Without them a post is `post` by `post` |
| `postEndDistance` | how far the first and the last post stand from the ends of the line, in mm, where the element gives no `posts.at`. Absent: 0 |
| `belowBase` | how far the posts and the infill reach below the base, in mm, for side mounting. Absent: 0 |
| `topRail` | the top rail's shape: `rectangular` (`railWidth` by `railDepth`, the default), `round` (diameter `railWidth`), `halfRound` or `flat` |
| `panelPattern` | a panel's pattern, for the kind `panel`: `solid`, `perforated` or `corrugated` |
| `corner` | how rails and infill meet at a corner of the line: `mitre` or `square`. Absent: the rails run past each other |

On the element's `railing`:

| Member | Meaning |
|---|---|
| `insets` | per edge, by edge id: that edge's inset where it differs from `inset` (outline hosts only) |
| `posts.at` | the posts' positions: distances in mm along the railing's line from its start, measured in plan, in increasing order. They do not move when an inset changes |
| `posts.omit` | a list: `["start"]`, `["end"]` or both: the ends that have no post of this railing, because a neighbouring railing's post or a wall carries them. With `posts.at`, the list of posts is complete, and `omit` only says which ends a neighbour carries |

**Not every member for every kind.** A type's `railing` MUST give the members its kind requires, and need not give the others. (Until 14 October 2026 every railing type gave all thirteen, even those its kind does not use: `panel` for balusters, `handrailDiameter` without a handrail.)

| Member | Required for |
|---|---|
| `kind`, `height`, `postSpacing`, `railWidth`, `railDepth`, `material` | every kind |
| `post` | every kind, unless `postWidth` and `postDepth` are both given |
| `baluster`, `gap` | `balusters` |
| `clearance` | `balusters`, `glass`, `panel` |
| `panel` | `glass`, `panel` |
| `handrailDiameter` | a type whose `handrail` is more than 0 |
| `handrail` | none: absent means 0, no handrail on the walking side |

A member a kind does not use MAY be present, and readers ignore it for that kind. `gap` is the widest clear opening between balusters, not their spacing centre to centre. The type schemas state this table; the element schema states that `posts.omit` names each end once.

**Posts are normative when given.** If `railing.posts.at` is present, a post stands at each distance it lists along the railing's line, and nowhere else: every reader, compiler and check places them there (Part IV, 11.2). Otherwise the post layout is reference behaviour, within the type's `postSpacing` and `postEndDistance`. A supplier's tool writes its post layout with an `update` of `posts.at`, as framing members are materialised (section 10): the product's rules stay in the supplier's tool, and their result is in the record, so every tool shows the same posts. When the line changes later (the host is reshaped), `posts.at` stays as written, and a check reports a post that is off the line or a span longer than `postSpacing`.

A side-mounted railing with balusters on three edges of a balcony slab, the `railing` of its type and of its element (compacted; a synthetic example):

```json
{
  "baluster": 20, "belowBase": 300, "clearance": 0, "corner": "mitre", "gap": 98,
  "height": 1150, "kind": "balusters", "material": "Aluminium", "mount": "side",
  "postDepth": 50, "postEndDistance": 200, "postSpacing": 1200, "postWidth": 30,
  "railDepth": 35, "railWidth": 50, "topRail": "halfRound"
}
```

```json
{
  "edges": ["slab-balcony-a:edge:3", "slab-balcony-a:edge:0", "slab-balcony-a:edge:1"],
  "inset": -25,
  "insets": {"slab-balcony-a:edge:1": -40},
  "posts": {"at": [200, 1308, 1525, 2633, 3525, 4642, 5675, 5900]}
}
```

The type gives no `panel`, `handrail` or `handrailDiameter`: its kind does not use them. The supplier's product identity can travel in the type's `catalogSource` (section 9.2); its article numbers stay in its own folder, or in a declaration authoritative for `fabrication` if it chooses to share them (section 12.1).

**Details decided with the reference implementation** (11 October 2026):

- **A corner where two edges have different insets** is where the two moved edge lines cross; if they are parallel, or cross farther than four times the larger inset from the corner, it is halfway between their ends. When every edge of the run has the same inset, a corner moves along its bisector instead (Part IV, 11.2, rule 3): the two rules agree except at turns sharper than about 151°, where the moved lines cross farther than four times the inset from the corner, and the bisector rule stops there, at four times the inset.
- **A post** rises to the underside of the top rail, and the top rail carries the railing to its `height` (Part IV, 11.2).
- **A position in `posts.at` beyond the line's end** is not refused: a check reports it, as it reports a span longer than `postSpacing`.
- **`panelPattern`** is a property of the panel; a compiler need not draw it.

Reference: `railingTypeSchema` and `railingElementSchema` in `src/core/elements/railings.ts` (the members, and what each kind requires).

*Note (informative).* Stomra's pull request #786, live in Stomra since 12 October 2026 (`main` `1e1089db`), implements every member above, side mounting in the bodies (posts and infill reaching `belowBase` below the base, with a fixing block on the edge face), rectangular posts, the rail shapes and the corner cut; a railing without the new members draws as before. Stomra's commands accept and validate them.

*Note (informative).* Three questions about railings were deferred to 0.2 by the steward on 15 October 2026 (open questions, 18): whether a hosted railing keeps the profile its writer copies from its host, or the profile becomes derived and is no longer stored; railings on a slab edge element or on a wall's top (a parapet); and railings on stairs. Meanwhile rule 4 stands: a hosted railing's `profile`, `levelId` and `offset` are its writer's copy, rewritten with its host, and a reader follows the host where they disagree. A railing on a slab edge element or on a wall's top had no host in 0.1 until 24 October 2026: it stood on a line of its own (rule 2). The line and the posts of a railing on a stair are reference behaviour (Part IV, 11.2). On 21 October 2026 the steward decided what 0.2 says (open questions, 18): the line of a railing on an outline is derived, and its stored `profile` deprecated; a wall's top and a slab edge element join the hosts, once the reference implementation builds them; and a railing on a stair stays reference behaviour until a stair's flights have a class-A rule. The reference implementation builds the two new hosts since Stomra 643 (live since 23 October 2026, `main` `acf86581`), as open questions, 18 records, and on 24 October 2026 the steward took them into 0.1 (below). Section 12.2.1 deprecates the derived line's profile in 0.2.

**On a wall's top and along a slab edge element.** By the steward's decisions of 21 and 24 October 2026 (open questions, 18), in 0.1 since 24 October 2026, as the reference implementation builds them since Stomra 643. Part IV, 11.2 derives the line (its rules 8 to 11).

1. A railing on a wall's top names the wall in `hostId`; a railing along a slab edge element names the element. Both join the references that MUST resolve (section 7).
2. On these hosts, `railing.side`, `railing.edges` and `railing.insets` MUST NOT be given (rule 2 above). `railing.inset` and `railing.posts` (`at`, `omit`) mean what they mean on an outline.
3. `railing.inset`, from −1000 to 1000 mm, is the distance of the railing's line from its host's outer face towards its inside: from a wall's exterior face (Part I, 5.1), from a slab edge element's outer face. Without it, the line runs along the middle of the host's top.
4. Its `levelId` is the wall's level, or the level of the slab whose edge the element follows; its `offset` is 0. Its line is derived: its `profile` is not read. 0.1's schemas ask for a profile of one point or more, so a writer writes one point there, which means nothing; from 0.2 it may leave the profile out, as the derived line of a railing on an outline lets it (open questions, 18).
5. Its walking side is a wall's interior (the right of its directed baseline), or the side of the slab. It has no member of its own: a wall turned round turns the railing with it, as its layers turn (Part I, 5.1), and a given `inset` and `posts.at` are read from the turned wall's faces and ends.
6. When its host is deleted, the railing goes with it, as a hosted element does (Part V, 5, rule 1).

*Note (informative).* The choices of 24 October 2026 follow the reference implementation: a railing on a wall's top takes no `side` (rule 5), since a wall already says which side is its interior; its line runs along the middle of its host's top by default (rule 3), where an outline's default inset is 50 mm, because a parapet's or an upstand's top is narrow and the railing stands on it; and its `profile` becomes optional with the derived line (rule 4). The reference implementation writes one point there, `[[0, 0]]`, as rule 4 says.

### 12.2.1 A hosted railing's profile

*Part of 0.2. OpenEik 0.1 had it as the proposal `railings`: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), every railing carries a `profile`, as 0.1's element schema asks, and rule 4 of section 12.2 stands. The section adds no file and no member.*

By the steward's decisions of 21 and 24 October 2026 (open questions, 18). The line of a railing on the outline of a slab or a shaft, on a wall's top or along a slab edge element is derived (Part IV, 11.2), a class-A rule; from 0.2 nothing of it is copied into the record.

1. **On an outline.** The `profile` of a railing on the outline of a slab or a shaft is no longer its writer's copy: the copy is deprecated (Part I, 9.6). A reader derives the line from the host and its edges, and ignores a profile it finds; a writer does not rewrite it when the host changes, and SHOULD NOT write it. Such a railing MAY leave out `profile`. Its `levelId` and `offset` stay as rule 4 of section 12.2 writes them: the host's level, for a shaft with a top constraint that constraint's level, and 0.
2. **On a wall's top or along a slab edge element.** A railing MAY leave out `profile`, which means nothing there (section 12.2, "On a wall's top and along a slab edge element", rule 4): the one point 0.1's schema asks for is deprecated, and a writer SHOULD NOT write it.
3. **On a stair.** A railing on a stair keeps its copy: its `profile`, `levelId` and `offset` are written from its host as rule 4 of section 12.2 says, and a reader gets no heights from them, until a stair's flights have a class-A rule its line can follow. A railing with no host has its profile as its line, as before.
4. **What a validator checks.** A railing that names a host and no `railing.side`, which so stands on no stair (rule 2 of section 12.2), need not carry a `profile`. A validator SHOULD warn about the `profile` of a railing on an outline, a wall's top or a slab edge element (`railing-profile`), and a writer that reads one removes it.

A railing on three edges of a balcony slab is `{"category": "railing", "hostId": "slab-balcony-a", "levelId": "level-1", "offset": 0, "railing": {"edges": ["slab-balcony-a:edge:3", "slab-balcony-a:edge:0", "slab-balcony-a:edge:1"], "inset": -25}, …}`, with no `profile`; its line is the run of those edges, moved by its inset.

*Note (informative).* The reference implementation writes and rewrites the copy of a railing on an outline, as 0.1 asks, and one point, `[[0, 0]]`, for a railing on a wall's top or along a slab edge element; its compiler draws every hosted railing from its host already. It stops writing the profile when it writes 0.2.

## 13. Views and detail items of building services

Views and their detail items, the 2D items of a view, are records of the documentation profile (sections 1 and 8; Part III, 10), held to their schemas. This section says what the members of heating, ventilation and plumbing drawings mean, by the steward's decisions of 23 and 24 October 2026 (open questions 48 to 50): a view's filter by system, frames and the white ground of texts and tags, the tags of pipes, ducts and terminals, the room tag, and symbols with lines joined to their ports. What a view shows is derived, and drawing it is the application's (section 10).

### 13.1 A view's filter by system

A view's `visibility.systems` shows some systems and hides the others: `{mode, ids}`.

| Member | | Meaning |
|---|---|---|
| `mode` | required | `show`: the view shows the systems named, and hides the others; `hide`: it hides the systems named, and shows the others |
| `ids` | required | 1 to 40 systems: a system of section 6.9 (`coldWater`), or a system and its number after a colon (`heating:20`) |

A system without a number names that system under every number; with one, under that number only. `heating:1` names the heating runs whose `mep.number` is 1 or absent.

- **A pipe or a duct** is hidden when its system and number are.
- **A component in a pipe or a duct** (section 6.9) is hidden when its run is.
- **A duct part that names no duct** (section 6.9) goes with the duct it stands on, where it stands on one, and is otherwise another terminal; the reference takes the nearest duct within 300 mm in plan.
- **Another terminal** is hidden when every system it serves is hidden: the systems and numbers of the runs whose ends join it (`mep.joins`), else the systems of its type and its connections, under any number.
- **Their tags** go with them, and so do their bodies in a section or a 3D view.

A dependent view takes its parent's filter with the rest of its visibility. How a drawing shows what the filter hides, left out or in halftone, is the application's.

### 13.2 Frames and the white ground

A detail item's `textFrame` draws a frame round its text: `{shape, margin?, weight?}`.

| Member | | Meaning |
|---|---|---|
| `shape` | required | `rectangle`, `rounded` (a rectangle with rounded corners), or `none`: no frame, where the view would give one |
| `margin` | optional | the gap between the text and the frame, in paper mm, from 0 to 10; absent, 1 |
| `weight` | optional | the frame's line width, in paper mm, from 0.05 to 2; absent, 0.25 |

A view's `display.textFrame` is the frame of its texts, leaders and tags that give none of their own; `display.mepTagFrame` the frame of the plan's automatic tags of pipes, ducts and terminals (13.3). An item's own frame wins over its view's, and `none` leaves out the view's. The frame is measured from the text's extent, so it follows the text, its size and the view's scale: its sides lie `margin` outside the text's box, and a rounded frame's corners have a radius of half its height, at most 1.5 paper mm. A leader leaves from the frame's edge.

A detail item's `textBackground`, `opaque` or `transparent`, says whether its text stands on a white ground that hides what lies under it; a view's `display.textBackground` is that of its texts, leaders and tags that give none of their own, and without either the ground is transparent. A dimension style has its own (`textBackground`), as in 0.1.

A frame and a ground mean something on a text, a leader and a tag; on another item they are kept, and draw nothing.

### 13.3 Tags of pipes, ducts and terminals

**The plan's automatic tags.** A plan tags its pipes, ducts and terminals by itself: a run's designation (its system, number, material code and size) and a terminal's code, as the application writes them. A view's `display.mepTags` says how many: `each`, the default, tags every run and terminal; `perRun` tags one run of each chain of runs joined end to end with one designation and size, the longest in plan, and every terminal; `none` tags none. A view's `mepTagEdits` keeps, by an element's id, what was done to that element's automatic tag in the view: `offset`, where it stands, in the view's mm from where the plan puts it; `hidden`, `true` where it is not drawn; and `elbow`, `true` where its leader runs at right angles, along to the tag's height and then across to its edge. The tag stays the element's: it follows the element, and can be moved one by one.

**Lined-up tags** are drawing behaviour: an application's command (the reference's `tags.lineUp`) stands the tags of chosen elements in a column or a row outside the building, and writes what it did as each tag's `offset` and `elbow` in `mepTagEdits`. It adds no member.

A terminal type's `terminal.tagSize`, `false`, leaves its size out of its elements' tags; it is 0.2's (section 14.6).

**A tag placed by hand** is a detail item of the kind `tag` (section 1). Its `tag.field` `mep` reads the element's automatic tag: a run's designation, a terminal's code.

**Tag templates.** A view's `display.mepTagTemplates` says what its tags of pipes, ducts and terminals read, `{pipe?, duct?, terminal?}`, each `{rows}`: one to three rows, each of 1 to 120 characters. A template is the view's data: it travels with the view, so that another application reading it knows what the view's tags are to read. Its rows are written in the application's own language of fields, an application value (section 9.2; open question 49). A tag's text, the template filled in, is derived, and never stored.

*Note (informative).* The reference's rows hold fields in braces, a part in square brackets being left out when a field in it is empty: a run's `{system}`, `{number}`, `{number:00}`, `{code}`, `{size}`, `{insulation}` and its height `{z}`, `{z:UK}` or `{z:ÖK}` above the floor (the Swedish marks for a run's underside and top); a terminal's `{code}`, `{size}`, `{flows}`, `{power}`, `{lph}`, `{kv}`. The templates of a project are in its `standards` (`project.standards.mep.tagTemplates`, by category and by system), and a view's win over them. Without a template, a tag reads as it did.

### 13.4 The room tag

A tag of the field `room` tags a room: its `tag.elementId` names the room. It stands at the room's label point, follows the room, and may have a frame (13.2). The tag is model data, a detail item of its view as any tag is; what it reads is derived: the room's number and name; the codes of the fixtures in it, counted; its air terminals by code and size; and its supply and extract air flows summed, in l/s. A view that hides the ducts leaves the air out, and one that hides the pipes the fixtures. Components in a pipe or a duct, and duct parts that name no duct, are not the room's.

### 13.5 Symbols

A **symbol** is a detail item that places a symbol of a library: a valve, a pump, a heat exchanger, a fan, a damper, as a flow schematic draws them. It is drawn at paper size, the same size on the sheet at any scale, with its fill and its texts. Its **ports** are the points where lines join it. Symbols are made for schematics, drawn in drafting views; a symbol may stand in any view.

A detail item of the kind `symbol` stands at its `a`, and its `b` is the same point. Its `symbol`:

| Member | | Meaning |
|---|---|---|
| `name` | required | the symbol, by its name in the application's library: 1 to 40 characters, an application value |
| `rotation` | optional | its turn about `a`, in degrees counter-clockwise, from −360 to 360; absent, 0 |
| `mirror` | optional | `true`: mirrored across its own vertical axis before it is turned |
| `texts` | optional | its texts, by the library's slots: each slot's name 1 to 20 characters, its text at most 40, such as `{"tag": "P201"}` |

**The library is the application's.** A library gives each symbol its shapes, its texts' slots and its ports, in paper mm about its insertion point, `y` up; OpenEik lists none (section 8, rule 3; open question 50). A point `(x, y)` of a symbol stands in the view at `a + scale · R(rotation) · (±x, y)`: `−x` where it is mirrored, `R` the turn counter-clockwise, `scale` the view's. An application that does not know a symbol's name keeps the item, and may draw it as a box with its name.

**Lines joined to ports.** A line's `connect` joins its ends to ports: `{start?, end?}`, each `{detailId, port}`, the symbol item and the name of its port in the library (1 to 40 characters, an application value). The line's first point is its start, and its last its end.

- **A joined end stands on its port.** Its stored point is the port's, where the view draws it: the stored line is what is drawn.
- **When a symbol moves**, turns or is mirrored, or its view's scale changes, a writer moves each end joined to its ports with them.
- **When an end is moved off its port**, or the symbol goes, the end lets go: the writer takes it out of `connect` and leaves the end where it is.
- **An end put on a port**, within 0.6 paper mm of it, MAY be joined to it by a writer.

The line from a pump's outlet to a valve's inlet, in a schematic at the scale 1:

```json
[
  {
    "a": [100, 100],
    "b": [100, 100],
    "color": "#000000",
    "id": "pump-a",
    "kind": "symbol",
    "symbol": {
      "name": "pump",
      "texts": {
        "tag": "P201"
      }
    }
  },
  {
    "a": [150, 100],
    "b": [150, 100],
    "color": "#000000",
    "id": "valve-b",
    "kind": "symbol",
    "symbol": {
      "name": "isolatingValve"
    }
  },
  {
    "a": [104, 100],
    "b": [146, 100],
    "color": "#000000",
    "connect": {
      "end": {
        "detailId": "valve-b",
        "port": "in"
      },
      "start": {
        "detailId": "pump-a",
        "port": "out"
      }
    },
    "id": "line-a",
    "kind": "line"
  }
]
```

*Note (informative).* The reference implementation's library has 35 symbols of Swedish heating and ventilation schematics, named in English: valves, the circulation pump, a heat exchanger, an expansion vessel, a tank, a radiator, meters and sensors, filters, fans, heat recovery, coils, dampers, a silencer, terminals, a hood and grilles. Each port stands at the end of a short stub, named `in`, `out` or after its place on the symbol. It works the joins out with every `view.upsert`, as 13.5 says, and so never writes a breach of 13.6, rule 7. Its schema holds a symbol's name to its library's 35, which 0.1's does not (13.6, rule 9).

### 13.6 Rules

1. Each of a view's `visibility.systems.ids` MUST be a system of section 6.9, alone or with a number from 1 to 99 after a colon, written without a leading zero. The schemas state this rule: `^(<system>|…)(:[1-9][0-9]?)?$`.
2. A `textFrame`, a `display.textFrame` and a `display.mepTagFrame` MUST give `shape`, and their members MUST be within the ranges of 13.2. A `textBackground` MUST be `opaque` or `transparent`. The schemas state this rule.
3. `display.mepTags`, `mepTagEdits` and `display.mepTagTemplates` MUST be of the shapes of 13.3; the schemas state it, and mark each row of a template `"x-eik-application": true`. A reader MUST keep a template's rows unchanged, and MUST NOT need to read them: an application that does not know a template's language draws the tags as it would without it.
4. The element that a tag of the field `room` names MUST be a room of the model.
5. A detail item of the kind `symbol` MUST carry `symbol`, and a detail item of another kind MUST NOT. The schemas state this rule.
6. A detail item of another kind than `line` MUST NOT carry `connect`. The schemas state this rule.
7. Each `detailId` of a line's `connect` MUST name a detail item of the kind `symbol` of the line's own view.
8. A writer MUST keep a joined end on its port, as 13.5 says, or let it go.
9. A symbol's `name`, a port's name and a text's slot are the application's: a validator MUST NOT refuse one because a library does not hold it, and a reader keeps them unchanged.

Reference: `systemFilterSchema` in `src/core/views/systemFilterSchema.ts` and `systemFilterHides` in `src/core/views/systemPlans.ts` (Stomra work 611), `withSystemFilterIds` and `looseSystemIds` in `systemFilterSchema.ts` (669); `textFrameSchema`, `frameOf` and `frameBox` in `src/core/annotations/textFrame.ts` (608); `mepTagModes`, `mepTagEditSchema` and `taggedRuns` in `src/core/elements/mepTagSchema.ts` and `mepTags.ts` (its work 540); `viewTagTemplatesSchema` in `src/core/elements/mepTagTemplateSchema.ts` and the filling in `mepTagTemplate.ts` (621); `roomTagRows` and `roomTagPoint` in `src/core/elements/roomTag.ts`, and `tagItemSchema` in `src/core/sheets/tagItemSchema.ts` (622); `tagLineUpCommandSchema` and `linedUpView` (623); `symbolItemSchema` and `lineConnectSchema` in `src/core/sheets/symbolItemSchema.ts`, the refinement of a detail item in `src/core/sheets/documentation.ts` (rules 5 and 6), `symbolGeometry` in `src/core/annotations/symbolLibrary.ts`, and `connectedDetails` and `withConnectedSymbols` in `src/core/annotations/symbolConnections.ts` (634).

*Note (informative).* Since Stomra work 669 (24 October 2026) the reference implementation writes a filter's ids as rule 1 says: it writes `heating:05` as `heating:5` with every `view.upsert` and when it opens a model, and refuses an id still out of form, such as `heating:0`, in a new command, never in a replay. Its read schema still takes one or two digits after the colon, so that a model or a log of before opens. It hides a component in a pipe with its pipe, by the pipe's system and number, as 13.1 says, and since its work 676 a component in a duct with its duct; before 669 a plan filtered to `heating:30` showed a valve of a pipe of `heating:20` whose pipe it hid (open questions, section 2, rows 30 and 31).

## 14. Members of views, detail items and detail families

*Part of 0.2. OpenEik 0.1 had it as the proposal `adopted`: in a model of 0.1, read by 0.1's rules (Part I, 9.6, rule 3), the members of this section are members a schema of 0.1 does not know, which a reader keeps (Part I, 10). The section adds them, the cut pattern `clt` (section 4, rule 4), the reference a bolt group's `bolts.through` holds (section 7), a slab edge element's `notches` (Part IV, 4.6, rules 12 to 14), and no file. Their schemas, in `schemas/0.2/adopted/`, are generated from the reference implementation's, as the schemas of 0.1 are.*

By the steward's decision of 30 October 2026 (open questions, 115). The reference implementation writes these members, and another tool needs them to read, draw, check or print the building, so they join 0.2. What only the reference implementation's own interface reads, it writes under its prefix from 0.2 (Part I, 11.5).

### 14.1 Elements cast in one pour

1. An element MAY carry `castWith`, the id of another element that it is cast with in one pour, as a foundation poured with the slab on it. It SHOULD name an element of the model (section 7), and MUST NOT name the element itself: a validator warns of one that does (`cast-with`).
2. Where a section cuts both elements, their cut regions of one material, at one depth of the cut, are drawn as one region: no line is drawn where they meet, and their regions of other materials keep their lines. It changes no body and no quantity, and the two stay two elements.

### 14.2 A layer drawn with a detail family

A drawing of a type's build-up (a view's `assembly`, section 14.3, rule 4) draws each layer where it is cut, by its material's pattern (section 4).

1. A layer (section 5.1) of a type of the category `slab`, `wall`, `foundation`, `roof` or `ceiling` MAY carry `drawing`, `{familyId?, typeId?, spacing?, offset?, rotation?, align?}`: the detail family its cut is drawn with in such a drawing, in place of its material's pattern. `familyId` SHOULD name a detail family of the model (`project/detail-families`), and `typeId` names one of that family's types; a validator warns of a `typeId` the family does not have, or one without a `familyId` (`layer-drawing`). Without `familyId` the layer draws nothing of its own where it is cut, so that what the layer beside it draws shows there, as the battens a roof's tiles hang on.
2. **The copies.** The family is drawn along the layer again and again, each copy centred on its place: at `offset` mm from the layer's start (0 where it is absent) and every `spacing` mm from there, both ways, as far as a copy reaches into the layer; where `spacing` is absent, the copies lie side by side, each its own extent along the layer from the next. Each copy is turned by `rotation` degrees, as a detail item's rotation turns it. Across the layer it lies on the layer's middle, or, with `align` `inside` or `outside`, its box against the layer's inside or outside face; a family with an insertion point (section 14.5) hangs by that point instead, on the layer's middle, inside or outside face.
3. **Its axes.** The family's x runs along the layer, a roof's from its eave up and a wall's from its foot up, and its y out of the building.
4. A layer's drawing changes no body and no quantity.

### 14.3 A view

1. **An electrical plan's devices.** A plan's `display.electrical` says which electrical devices (Part XIII, 2) it draws: `power`, every device but a data outlet, a TV outlet, a speaker and an access point (the kinds `dataOutlet`, `tvOutlet`, `speaker` and `accessPoint`); `data`, those four kinds alone; absent, all of them. On a view of another kind it means nothing, and a validator warns of it there (`adopted-kind`).
2. **Level heads.** `display.levelLabels`, `false`, draws a section's, an elevation's or a callout's level lines without their heads, the mark that gives the level's name and its elevation; absent or `true`, with them.
3. **The sides of a build-up.** `display.sideMarks`, `false`, leaves out the marks that a drawing of a type's build-up (rule 4) sets beside its sides, outside and inside for a wall, top and underside for a horizontal type; absent or `true`, they are drawn. Their words are the drawing application's.
4. **A build-up's pitch.** A view's `assembly`, `{typeId, mode}` in 0.1, draws the build-up of the type `typeId` names: cut vertically (`vertical`), horizontally (`horizontal`), or as an isometric (`isometric`). Its `pitch`, from 0 to 75 degrees, is the pitch a roof type's vertical cut is drawn at, turned about its middle and rising to the right. Absent, it is the pitch every roof of the type has, the slope of its first slope-defining edge to 0.1 degrees (0 for a roof without one), where they all have the same, else 0, level.
5. **Line weights.** A view's `lineWeights`, `true`, says that the view is drawn on screen with its line weights; absent, it is drawn in thin lines, every line one device pixel wide. It is the view's own, so that it is the same for everyone who opens the view. A print draws a view's line weights whatever it says.
6. **A 3D view's perspective camera.** A 3D view MAY carry `camera`, `{position, target, fov}`: where its perspective camera stands and the point it looks at, each `[x, y, z]` in mm in the model's axes, and its vertical field of view in degrees, from 10 to 120. `position` and `target` MUST be at least 1 mm apart; a validator reports a camera whose two points are not (`camera-points`). A tool that shows the view in perspective, or renders it, looks so. A sheet draws a 3D view without perspective, by the camera of its `origin`, `direction` and `axonometricElevation`, which `camera` leaves as they are, so a 3D view that a sheet places SHOULD NOT carry `camera`. On a view of another kind it means nothing (`adopted-kind`).

### 14.4 Detail items

1. **A revision cloud.** A detail item of the kind `line` MAY carry `cloud`, `{arc, inward?, revision?, tag?, tagHidden?}`, which draws it as a revision cloud. Its points (its `points`, else `a` and `b`) are the cloud's corners, in order round it, and it is closed. Between them it is drawn as scallops of `arc` mm on the paper, from 1 to 50, so that they keep their size at any scale, bulging out of the cloud, or into it where `inward` is `true`. `revision`, at most 20 characters, is the mark of the revision the cloud belongs to, which its tag shows: a triangle with the mark in it, centred on `tag`, a point of the view, else on the cloud's first corner. The tag is not drawn where `tagHidden` is `true` or the mark is empty. A reader that does not draw clouds draws the corners as a closed line. On an item of another kind `cloud` means nothing (`adopted-kind`).
2. **A schedule's headings.** A schedule's `schedule.headings` gives its columns' headings by the field each column shows (`number`, `name`, `area` and the others its schema names), each of 1 to 80 characters, in place of the headings the drawing application gives them; a column whose field it does not name keeps the application's.
3. **A legend's heading.** A legend's `legend.title`, of 1 to 120 characters, is its own heading, in place of the one the drawing application gives its scheme.
4. **A build-up text.** A detail item of the kind `leader` MAY carry `buildUp`, `{typeId, uValue?}`, which makes its text the build-up of the type `typeId` names: a line for each of its layers, in the order of section 5.1, rule 6, a wall's exterior first and a horizontal type's top first, each with its thickness and its name; and above them the type's U value (section 5.5), unless `uValue` is `false`. The text is worked out from the type each time it is drawn, so that it follows the type; its words and its layout are the drawing application's. Where the item gives its own `text`, that text is drawn instead. `typeId` SHOULD name a type of the model that has layers: a validator warns of a type without (`build-up-type`). On an item of another kind `buildUp` means nothing (`adopted-kind`).
5. **An item that follows a group's instance.** A detail item of a plan MAY carry `follows`, the mark of the instance of a group (section 10) it moves and turns with: the id of a copy, or of an original whose kept elements are its own instance (its `frame` absent or its own id). Only a free item follows an instance: neither a tag, a view reference, a schedule or a legend, nor an item with `tag`, `dimensionReferences`, `hatchHost`, `host` or `modelPoint`, each of which follows what it is tied to. On such an item, or on an item of a view that is no plan, `follows` is not read, and a validator warns of it (`follows-free`). It SHOULD name an instance of the model.
6. **Moving with it.** A command that moves or turns an instance (`copy.move`, Part V, 2.1) moves and turns the items that follow it, as its elements move: each is turned by the change of the instance's `turn` about the point its `at` named before, then moved by the change of its `at`. Where the instance goes (`copy.remove`, `copy.unique`, `original.dissolve`), an item that follows it stays where it is, and the command takes its `follows` away.
7. **A region cut by groups.** A detail item of the kind `region` in a plan MAY carry `cutBy`, 1 to 200 marks of instances of groups (rule 5), whose floors are taken out of it where it is drawn. An instance's floors are, in plan, the outlines of its slabs and foundations on the plan's level; where it has none there, those on the lowest level it has any on; where it has none at all, the convex hull of what it covers in plan. They are worked out where the instance stands each time the region is drawn, so that a hole moves with its instance. The region keeps its own outline, and is drawn as that outline less theirs. Each mark SHOULD name an instance of the model; where an instance goes, its mark is taken out of `cutBy`, and its hole closes. On an item of another kind `cutBy` means nothing (`adopted-kind`).
8. **A text that measures nothing.** A detail item of the kind `text` MAY carry `notDimension`, `true`: its words read as a number, and measure nothing, so that a check that holds a drawing's written dimensions to the model leaves it out. An item of another kind MUST NOT carry it (`not-dimension`).

### 14.5 A detail family's insertion point

1. A detail family MAY carry `insertion`, `[x, y]`, the point of the family that a placed copy hangs on: a single item of the family at its place, each copy of a repeating detail along its path, each copy of a layer's drawing (section 14.2). Each of the two is in the family's own mm, a number or bound to its parameters as a sketch's points are (`{parameter, factor?, offset?}` or `{terms, offset?}`); a parameter it names MUST be one of the family's (`insertion-parameter`). Without `insertion`, a single item hangs on the family's origin, and a repeated copy on the middle of its box, so that an edit that changes the box moves the repeated copies; with it, an edit never does.

### 14.6 A terminal's tag without its size

1. A terminal type's `terminal.tagSize`, `false`, says that its elements' tags give no size: the plan's automatic tags and a template's `size` field (section 13.3). Absent or `true`, they give the size of the type's connection of the terminal's own system, else of its first connection. A component in a pipe or a duct (section 6.9) is tagged with its run's size whatever its type says.

Reference: `castGroups` in `src/core/geometry/sectionOutline.ts` (Stomra work 508); `layerDrawingSchema` and `layerCopies` in `src/core/assemblies/layerDrawing.ts`, `roofTypePitch` in `assemblyViews.ts`, and `buildUpSchema` and `buildUpText` in `src/core/sheets/buildUpSchema.ts` and `src/core/assemblies/buildUpText.ts` (its work 556); `viewElectrical` in `src/core/views/viewVisibility.ts` and `electricalPart` in `src/core/elements/electrical.ts` (537); `levelLabels` and `sideMarks` in a view's `display` (651); `lineWeights` in `viewSchema` of `src/core/model/model.ts` (706); `perspectiveCameraSchema` in `src/core/views/perspectiveSchema.ts` and `perspectiveOf` in `perspectiveCamera.ts` (747); `revisionCloudSchema` and `cloudTag` in `src/core/annotations/revisionCloudSchema.ts` and `revisionCloud.ts` (523); `canFollow` and `moveFollowers` in `src/core/model/groupDetails.ts` (603); `instanceFloors` in `src/core/annotations/regionCuts.ts` (678); the refinement of a detail item in `src/core/sheets/documentation.ts` (`notDimension`, 695); `familyAnchor` in `src/core/annotations/detailFamilies.ts` (556); `tagSize` in `terminalTypeSchema` and `terminalTag` in `src/core/elements/mep.ts` (543).

*Note (informative).* The reference implementation writes every member of this section as it says, and reads `lineWeights` on screen only: its prints draw the line weights. `schemas/0.2/adopted/` holds each member's schema as the reference implementation's own, and `tools/generate-schemas.mjs` keeps them out of the schemas of 0.1.
