# Part VIII. Bridges

*This part is informative in 0.1, except where it says MUST or SHOULD.*

OpenEik is a source format. IFC, glTF, DXF and PDF are delivery formats that a compiler produces from it, as a binary is produced from source code. A bridge is an exporter, or an importer, between OpenEik and one of them: section 1 prints IFC, and section 2, proposed for 0.2, reads it once to start a project.

An export is a print. Like a drawing printed on paper, it is a copy of the version it was made from: it is never the model's record, and two exports are never compared to find what changed, since the versions they came from are (Part VI, 4). [Part XVIII, 7](https://openeik.org/spec/0.1/review#7), proposed for 0.2 from plan 688, says so as rules, and says how an IFC names the version it was exported from, says to people that it is a print and who printed it, carries a short form of its fidelity report, and why its bytes depend only on the version, the bridge and its options.

## 1. IFC export

1. A bridge that exports IFC MUST export every element of the core categories that has a body, with its type, its level as a building storey, and its hosted openings as voids in their hosts.
2. A bridge MUST publish, with every export, a fidelity report: the IFC schema written, the counts of products, openings and types, and what the export leaves out or approximates.
3. A bridge SHOULD give every product a GlobalId derived from the project's id and the record's id, so that exporting the same model twice gives the same GlobalIds, and exporting a different project gives different ones.
4. A bridge MUST export a generic element (Part II, 6.6) with its declared bodies, as the IFC entity its `generic.ifcClass` names, else as `IfcBuildingElementProxy`. Where its IFC schema has no such entity, it exports `IfcBuildingElementProxy` and says so in its fidelity report.
5. A bridge SHOULD print the property sets an element or a type was imported with (`ifcSource.properties`, Part II, 6) with the object it prints for that record: a set IFC4 defines, and that applies to the object, under its name, each property its template defines typed as the template types it; every other property in a set whose name does not start with `Pset_`. Section 1.5 says how.
6. A bridge SHOULD write, on each IfcRailing it writes for a railing (Part II, 12.2), IFC4's standard property `Pset_RailingCommon.Height`: the railing's height, its type's `railing.height`, to which its top rail carries it (Part IV, 11.2, rule 5), as an IfcPositiveLengthMeasure in the units of its print. IFC4's template applies the set to an IfcRailing and its type, and types `Height` so. Where the bridge prints the sets the railing was imported with (rule 5), an imported `Pset_RailingCommon` joins this one, and its `Height` keeps this value (section 1.5, rule 5).
7. A bridge MAY take the property sets an application wants on the objects it prints, data the model does not hold, and write each with the object the export writes for its record (an element's on its product, a room's on its space, and a type's as the own sets of each IFC type it writes for it), each value untyped, by what it is: a text an IfcLabel (an IfcText where it is longer than 255 characters), a number an IfcReal and a truth value an IfcBoolean. It MUST NOT write such a set under a name that starts with `Pset` in any case of its letters or with `Qto_`, which IFC reserves to the property and quantity sets it defines, or with `OpenEik_` or `Imported_`, which this part reserves to a bridge's own sets and to imported ones (section 1.5, rule 4); nor under the name of a set it writes itself on that object, nor merge it into one. It leaves such a set out, and its fidelity report says so (rule 2). An application SHOULD name the sets it gives with a prefix of its own, as the reference implementation's are `Stomra_IdentityAndParameters`, `Stomra_Railing`, `Stomra_Framing` and `Stomra_Electrical`, so that they keep clear of IFC's, of the bridge's and of every other application's.

Why the project's id: it is part of the model (Part II, 2) and stable for the life of the project, as every record's id is (Part I, 6). Every version of the project, every alternative and every fork or copy of its folder carries the same project id, so an element keeps its GlobalId across them, and a model checked in IFC today can be compared with one exported a year later or from another line of work. A namespace taken from where the model is stored (a repository, a host) would change the GlobalIds when the model moves.

*Note (informative).* Rule 3 is a SHOULD. An element or a type imported from IFC carries the GlobalId it was imported with (`ifcSource.globalId`, in the element and type schemas), and a bridge that keeps it in its print, rather than derive one, lets a model go round from IFC and back with its GlobalIds: the reference bridge keeps it by default, and so does `ifcOfModel` of `@openeik/tools` since 26 October 2026, for an element, for the opening or the space it was imported as, and for a type, unless another object of the print has that GlobalId already. Where several records carry one GlobalId, as several types imported from one IFC type do, one object keeps it and the others get derived GlobalIds. So that every bridge gives it to the same record, that is the record whose key comes first in code-unit order, `elements/<id>` or `types/<id>` (an element before a type, and of two elements or two types the one whose id comes first), of those the print writes an object for, whatever the order it writes them in. `ifcOfModel` does so since 28 October 2026; before, the one it wrote first kept it.

*Note (informative).* Rules 6 and 7 were decided by the steward on 29 October 2026 (open questions 92 and 93). The reference bridge writes a railing's `Pset_RailingCommon.Height` so, from its type's `railing.height` (`railingIfcProperties` in `src/core/io/railingIfc.ts`), and `ifcOfModel` since the same day. A railing's own `height`, which it takes from its type and its payload (Part II, 6.2), is not what either reads: the height a railing's top rail carries it to is its type's (Part IV, 11.2, rule 5).

The reference bridge (`ek ifc`, `exportIfc` in `src/core/io/ifcExport.ts`):

- writes **IFC4**, with tessellated geometry, swept solids for profiled members, material layer sets for layered types, element types and property sets;
- derives each GlobalId as the IFC base-64 encoding of the first 128 bits of the SHA-256 of `<project id>:element:<element id>` (`<project id>:type:<IFC class>:<type id>` for types, such as `type:IFCWALL:type-wall-exterior-300`; `level:<level id>` for a storey; `opening:<door or window id>` for an opening), or keeps a GlobalId the element was imported with when asked to;
- returns a report with the schema, scope, counts (products, openings, types, polygons, swept solids, spaces), units and a list of warnings that say what is approximated.

For `examples/one-storey-house` the reference bridge reports IFC4, 10 products, 3 openings and 6 types.

*Note (informative).* A component (Part II, 6.2) takes its IFC class by its type, as the reference bridge writes it: steel plates and bolts as plates and fasteners, by the steward's word of 26 October 2026 on open question 73, and a weld as that decision names it. Each is read from the records alone: the type's `component.family`, the bolt it holds (`component.bolt`) and the element's own `bolts` or `weld`.

| A component | IFC4 class, and its type's | Predefined type |
|---|---|---|
| a plate: its type's `component.family` is `plate` | `IfcPlate`; `IfcPlateType`, NOTDEFINED | NOTDEFINED |
| a bolt group: it has `bolts`, and its type's family is `bolt` and holds a bolt (`component.bolt`) | `IfcMechanicalFastener`, its `NominalDiameter` the bolt's `diameter` and its `NominalLength` the group's `length` where it gives one; `IfcMechanicalFastenerType`, NOTDEFINED | by the bolt's `kind` and `nuts`: an `anchor`, and a `rod` with one nut, ANCHORBOLT; a `rod` with none, DOWEL; a `bolt` (or no `kind`), and a `rod` with two nuts (or none said), BOLT |
| a weld: it has `weld`, and its type's family is `weld` | `IfcFastener`; `IfcFastenerType`, NOTDEFINED | WELD |
| any other: a bracket, a hanger, a timber section, a cast-in item, a void form, an edge detail, and a bolt or a weld type's element without `bolts` or `weld` | `IfcBuildingElementProxy`; `IfcBuildingElementProxyType`, NOTDEFINED | NOTDEFINED |

The class is part of a type's GlobalId (`type:<IFC class>:<type id>`, rule 3), so a type whose elements take two classes is two IFC types. Where a bolt group gives no `length`, the reference works out the shortest bolt that fits from a table of ISO bolts that no record holds (open questions, 73), and `ifcOfModel` of `@openeik/tools` leaves `NominalLength` unset. With the proposal `components` (Part IV, 7.6, proposed for 0.2), a component's body is the one its frame's rules derive, and `ifcOfModel` writes it so, and writes a body an application gives over it where the compiler says part of it is reference behaviour, as a bolt's head, washers and nut are.

*Note (informative).* The plan named IFC 4.3, IFC 4 and IFC 2x3 as targets, and GlobalIds derived from a repository namespace. Decided on 11 October 2026: GlobalIds stay derived from the project id, for the reason above, and an IFC 4.3 export comes later; 0.1 specifies no IFC version, and the reference bridge writes IFC4.

*Note (informative).* `@openeik/tools` implements this bridge too, written from the specification (`ifcOfModel` in `packages/openeik/src/ifc.js`, and `openeik ifc`), by the steward's decision of 25 October 2026 that every application prints IFC the same way. It writes IFC4 from the bodies its compiler derives (Part IV): every element of the core categories that has a body, its type, its level as a storey and its doors' and windows' openings as voids, with the GlobalIds, classes and predefined types of the reference bridge, the systems and property sets of section 1.1, and a generic element as rule 4 says. A print names the model version it was made from, in a property set on its IfcProject, and holds no time but the one its caller gives, so that one version prints to the same bytes.

*Note (informative).* Since 26 October 2026 `ifcOfModel` also writes what Part IV gives no body, so that the reference implementation can print its clients' IFC with it: a stand-in built from the records alone, which its fidelity report says is approximated, and why (`packages/openeik/src/ifc-approximate.js`). A door or a window is the box of its opening, or its type's outline, through its wall's thickness, filling it; a component a box of its size at its insertion point; a slab edge element of a framing family its section's bounding rectangle along its host's edge, and a gutter on a barrel vault its section along its roof's edge; a stair an IfcStair of its shape with its parts under it, keyed as the reference bridge keys them: a straight stair's one IfcStairFlight, a solid of its treads, with its risers, treads, riser height and going; an L's or a U's two flights and its landing (an IfcSlab LANDING), or an L's two flights and its winders (an IfcStairFlight WINDER), laid out as the reference implementation lays them; a spiral's or a helical stair's one flight, the sector of a cylinder its treads sweep; a stair in balanced winders the box round its runs and its turn; a room drawn by hand an IfcSpace aggregated under its storey, its outline up to its height, with its base quantities, its coverings and its department and unit as zones. A beam's or a column's section rides on its type as an IfcMaterialProfileSet. Where its caller names a projected coordinate reference system, it writes an IfcMapConversion: the plan's origin at the site's latitude and longitude, projected by the transverse Mercator (Krüger's series), and the plan's x axis turned by true north and the grid's convergence. Since the same day it writes the ports of section 1.1: each terminal's connections as IfcDistributionPorts nested in it, and a port at each end of a run that its record joins to one of them (`mep.joins`, Part II, 6.9), connected to it by IfcRelConnectsPorts, with the reference bridge's GlobalIds, names, predefined and system types and flow directions; and how a door opens and a window is divided, IfcDoor's and IfcDoorType's OperationType and IfcWindow's and IfcWindowType's PartitioningType, from its type's `openingFamily` or `windowFamily` and its own `handFlipped` and `flipped` (section 1.2). Since the rules of open questions 74, 75 and 77 were written the same day, it writes slab edge elements and gutters with the bodies its compiler derives (Part IV, 4.6 and 4.7), mitred ends and buried pieces included, and each shaft's OPENING or RECESS in the hosts it cuts (section 1.3), and no longer calls them approximated. It still leaves out, and says so: a room placed by a point (without the proposal `rooms`, Part IV, 7.5), a component placed by a frame of its own or as a cast-in item, a plate, a bolt group or a weld, wall joins, the fittings between runs and a space's wall areas. For `examples/one-storey-house` it reports IFC4, 10 products, 3 openings and 6 types, as the reference bridge does; for `examples/l-house`, the reference bridge's 130 products, 13 openings and 18 types, with the same GlobalIds and classes; for a building with every pipe, duct and fixture type of the reference implementation's library, its 118 ports too.

*Note (informative).* Where those stand-ins come from no rule of this specification, a rule would let every bridge write the same body, and let the bridge of `@openeik/tools` stop calling it approximated. These were the places, which the steward decided on 26 October 2026 as open questions 71 to 79 proposed; the rules of 71 (the operation), 74, 75, 77, 78 and 79 (the plan's origin) are written into 0.1 (Part II, 2, 5.6 and 6.5; Part IV, 4.2, 4.6 and 4.7; sections 1.2 to 1.4), and the others come in 0.2: a door's or a window's own body (its frame, leaves and glazing, from its type's `windowFamily` or `openingFamily`; Part IV, 4.3 leaves it reference behaviour, and 4.3.1 proposes its rule, which `ifcOfModel` follows with the proposal `frames` since 28 October 2026, writing each part as an IfcShapeAspect with a material constituent of its own, and no longer calling it approximated); a component's body by its family, and its frame (`componentInverted`, `axes`, `upright`, a cast-in item's `insertAt`; Part II, 6.3); a slab edge element's section across its edge, its anchor (`slabTop`, `structureBottom`) and its mitred ends (Part II, 6.3, `miterEnds`; Part IV, 11.2, rule 9 rests on it); a gutter's section, its place at its roof's edge and overhang, its ends, and where a roof of its group buries it (Part II, 6.3, `roof.joinGroup`); a stair's risers where it gives none, its flights, landings and winders for every shape, its location line, and the construction its type names (Part IV, 7.2; 7.3 proposes the numbers, the location line and the layout of straight, L and U stairs with landings, and a cast concrete stair's bodies, which `ifcOfModel` follows with the proposal `stairs` since 28 October 2026, writing a concrete stair's flights and landing by rule and no longer calling them approximated); a room's boundary found from the walls round its point (Part IV, 2, class B), its ceiling under the floors and roofs above it, and the quantities that rest on its walls (Part IV, 7.5 proposes them, which `ifcOfModel` follows with the proposal `rooms` since 29 October 2026, writing an IfcSpace for every room with a boundary and a ceiling, its body its volume under its ceiling, with all eight of its base quantities, and leaving out, and saying why, a room with neither); a shaft's openings in the slabs it passes; the placement of a member's profile on its body; which point of the plan a site's latitude and longitude name, and a coordinate reference system, which 0.1 does not name (Part II, 2); the side a door type's `hinge` names, which the bridge, like the reference implementation, takes to be the opening's start along its wall for `left`, mirrored by `handFlipped`, for a door's operation type; and the frame of a terminal type's connection `at` and the places of a manifold's `outlets` (Part II, 6.9), which the bridge reads as the reference implementation does (x across to the left of the way the terminal faces, y along it, z up from its base), and the way a port flows on a system that is neither heating nor cooling, which section 1.1 leaves to the reference bridge. Open questions 71 to 79 put each but the ports' to the steward, with what the reference implementation does and a recommendation; a door's hinge and operation are in 71.

*Note (informative).* Since 26 October 2026 `ifcOfModel` also writes, with no new rule: with the proposal electrical, Part XIII, 10's classes and predefined types for devices, cable carriers and modelled cables, the standard property sets, and the circuits, one `IfcDistributionSystem` `LV` aggregating an `IfcDistributionCircuit` for each, its devices its members, its start its board's `Circuit#n` port and its legs connected port to port, keyed as the reference bridge keys them (`el-port:<element id>:<port>`, `el-circuit:<circuit id>`, `el-leg:<circuit id>:<to>`, `el-system:LV`; n by the natural order of designations, Part XIII, 3.2), a further leg from one device or board leaving a SOURCE port of its own, since IFC4 connects a port to one port at most; a rebar element as an `IfcReinforcingBar` and a mesh as an `IfcReinforcingMesh`, a bar set's first legs as rods and reinforcement in a layer as a sheet over its host's layer, approximated, since a bar's shape comes from a national shape code and Part IV gives reinforcement no body, and one on a host that Part II, 6.1, rule 6 does not allow left out, as the validator's error says; a plant as an `IfcGeographicElement` of its kind, and a road and a parking as the reference bridge's proxies, with stand-in bodies; and a railing on a stair as an `IfcRailing` aggregated under its stair, along its own line rising from the stair's base to its top. It still leaves out a terrain's ground and its gradings, a shaft's openings until the rule of open question 77 is in Part IV, and the framing members a framing family's rule would derive, which, with the proposal `framing`, it writes as section 1.6 says since 29 October 2026. Against the reference bridge on the four example projects of its comparison, every object both write has the same GlobalId and class, but for one of two slab types imported with one GlobalId, which the order of writing decided until the note on rule 3 said which record keeps it (28 October 2026).

*Note (informative).* Since 26 October 2026, by the steward's decision that the reference implementation and a host print IFC with one writer, `ifcOfModel(model, {bodies})` takes the bodies an application gives for the elements whose bodies Part IV does not derive yet, as results of the derived layer in the form proposed for 0.2 (Part IV, 1.1.1), keyed `elements/<id>` and `types/<id>`. It writes a given body where its compiler derives none, over its own stand-in, and only while the records the result names keep their hashes. Since 28 October 2026 it also writes a given body over a compiled one where the compiler reports some of the element not derived, as reference behaviour of class B or C (`not-derived`, Part IV, 2: a wall cut to a roof, a roof's windows, join group, square-cut edges or layers' extent, a column's or a beam's connection, its own holes or a bolt group's through it, and the like), or where a wall's end meets another wall that the result names among its sources, whose join is reference behaviour (Part IV, 7.1) and which the compiler leaves square; elsewhere it writes the compiled body, which the rules of class A derive whole, and the given one is not used. Its fidelity report says whose bodies are given, and by whom (`given`), and where they are written over a compiled body, why, beside what it approximates, and which results it does not use, and why. On the reference implementation's seven examples with their bodies given (its work 750, 28 October 2026), 161 of the 340 walls, roofs and columns both print differed by more than 1 % in volume or 5 mm in their box in IfcOpenShell's geometry, by up to 81 % and 566 mm; 7 do now, each within its box: five whose openings the two prints cut differently, and two whose sections' arcs they draw differently. An extrusion, a rod, a cylinder and a swept section are written as an IfcExtrudedAreaSolid of a polygon less its holes, of a circle, or of the section's IFC4 profile (its outline where IFC4 has none); faces as an IfcPolygonalFaceSet, or as an IfcTriangulatedFaceSet, with their normals, where they come in triangles or a face is not plane; and a type's representation as an IfcRepresentationMap of its IFC type, placed by an IfcMappedItem. One representation holds one kind of item, so an element's bodies are swept solids only where all of them are. A piece is a part of its element, aggregated under it and keyed as the reference bridge keys a stair's (`stair-part:<id>:<piece>`), of the class its piece names (an IfcStairFlight, an IfcSlab LANDING, an IfcMember STRINGER or POST); a framing member too, keyed as the reference bridge keys it (`framing:<member id>`, its type `type:<class>:framing:<predefined type>:<user type>:<section>`); and an element's parts, where its bodies name two or more (`part`, Part IV, 1.1.1), make its material an IfcMaterialConstituentSet, a constituent each, with an IfcShapeAspect of its bodies, as a type's representation's parts make its type's; where they name one part or none, it keeps the material it has without parts, its type's, since a set of one constituent adds nothing to it (open question 91). Until 29 October 2026 one part made a set of one constituent, as a plate's `plate`, a rod's `shaft` and an imported rafter's `part-0` did; a door's or a window's parts, and a railing's or a truss's, are several, and are written as before. With the reference implementation's own bodies of its garage example (its work 727), the print and the reference bridge's, framing included, have the same 493 objects, each with the same GlobalId, class, predefined type, property sets and quantities (to 0.05, their own property sets left out); and so, since components take their classes by their types (the note above), do those of its extension, its balcony and its steel frame, with 131, 181 and 668 objects, plates and bolt groups among them.

*Note (informative).* Since 29 October 2026, by the steward's decision on open question 93, `ifcOfModel(model, {sets})` takes an application's own property sets (rule 7), as it takes `bodies`: keyed `elements/<id>` and `types/<id>`, each `{<set name>: {<property name>: text | number | truth value}}`. It writes an element's on its product, or a room's on its space, after the sets it writes itself and the imported ones, and a type's as the own sets (HasPropertySets) of each IFC type it writes for it, after the imported ones; each value as rule 7 says, as it writes an imported value it does not type (section 1.5, rule 4); and the sets, and the properties in each, in the code-unit order of their names, so that the same sets print to the same bytes whatever the order of their members. It keys a set `app-pset:<record key>:<set name>` for the GlobalIds of rule 3, a type's `app-pset:type:<the IFC type's key>:<set name>`, since one type may be written as several IFC types. A set named as rule 7 forbids, or as a set it writes itself on that object, its own or an imported one under the name it takes there, is left out, not merged; so is a set with no name or no property, and a property that is no text, finite number or truth value; a key that names no object the print writes is not used, and is no error; and the fidelity report says each, as `application property sets` of its key. Without the option, every print is what it was.

*Note (informative).* With the proposal `framingLayout` (Part IV, 7.4, proposed for 0.2), since 29 October 2026, `ifcOfModel` lays each framed wall's members out itself and writes them as Part IV, 7.4.11 says: each an IfcMember or an IfcBeam (LINTEL) aggregated under its wall, keyed and typed as a given framing member is, its material the rule's timber. A member given for such a wall is not used, and its fidelity report says why. On the reference implementation's garage, guesthouse and villa, each of the 856 members has the GlobalId, class, name and tag of the reference's own print, and the three prints, and the print of the probe of Part IV, 7.4's note, pass IfcOpenShell's validation and buildingSMART's normative rules (release 1.0.2), checked on 30 October 2026. Since 30 October 2026 it also lays out each roof's truss layout and each floor's joists, and writes them as Part IV, 7.4.13, rule 15 and 7.4.15, rule 11 say: each truss an IfcElementAssembly TRUSS under its roof with its members under it, each anchor pair an IfcMechanicalFastener connecting its truss to its wall, and a floor's members under it. On the reference implementation's garage, second version, and its villa's frame, every truss, member and anchor pair has the GlobalId, class, name and tag of the reference's own print (360 and 821 framing members, 11 and 38 trusses, 22 and 65 anchor pairs), and both prints pass IfcOpenShell's validation and buildingSMART's rules with no issue, checked on 30 October 2026. Since 30 October 2026 it also frames a truss layout's hip ends (Part IV, 7.4.16): each hip truss, jack and girder an IfcElementAssembly TRUSS like any truss, and each hanger an IfcMechanicalFastener HANGER that connects no element; on the probe of Part IV, 7.4.16's note, every truss, member and connection, 2854 objects, has the GlobalId, class, name, tag and object type of the reference's own print, which passes IfcOpenShell's validation and buildingSMART's rules with no issue, checked on 30 October 2026.

*Note (informative).* Every IFC print of an OpenEik tool, the reference implementation's and a host's, is held to one suite, `tools/ifc-validate.mjs`, and to one bar, no issue: IfcOpenShell's validation against the IFC schema and its EXPRESS where rules, and, from a checkout of them, buildingSMART's normative rules, those of its validation service. The same tool compares two prints object by object, by GlobalId, class and predefined type (an occurrence's own, else its type's), then property sets and quantities, so that an application can hold its own print to the bridge's while it moves its classes over. The prints of the examples, and of the reference implementation's garage, extension, balcony and steel frame with their bodies given, pass IfcOpenShell 0.8.4's validation with no issue. Since 27 October 2026 they also pass buildingSMART's rules, release 1.0.2 of its ifc-gherkin-rules, run with IfcOpenShell 0.8.5, as do the prints of the reference implementation's installations, with the proposal electrical, and of its villa with a garage and a guesthouse: a typed occurrence leaves its predefined type, and a door's or a window's operation, to its type; a closed face set is one closed shell; a part is placed in its whole's placement; and a stair flight's numbers are in Pset_StairFlightCommon alone. Two rules of that release stop on errors of their own, whatever the file: PSE001 on a property set its table defines in Python's notation, Pset_StairCommon and Pset_ElectricalDeviceCommon among them, and GRP001 on any group that is not an IfcGroup itself, a system or a zone. A third, TAS001, stops on a face with voids whose voids have different numbers of points, since it reads them into one array; such faces come where an application gives a face with several holes, as a wall's with two openings of different shapes (since 28 October 2026, where such a wall's given body is written over its compiled one). The suite lists such a stop as a rule error, apart from the issues: it says nothing of the file.

*Note (informative).* Since 27 October 2026 `@openeik/tools` also prints a model as IFCX, experimentally (`ifcxOfModel` in `packages/openeik/src/ifcx.js`, and `openeik ifcx`), by the steward's decision recorded in open questions, 86: an IFCX file is a print too, never a record, and this one is proof that OpenEik maps to IFCX, not a promise of a format. It adds no rule. It writes buildingSMART's `ifcx_alpha`, which will change with its post-alpha format, and no conformance class covers it. It reads the bridge's IFC4 print back, not the model, so that both prints have the same objects, GlobalIds, classes, predefined types, property sets and bodies: each object's path is its GlobalId as the UUID it encodes, as buildingSMART's converter keeps it, and a body's a version-5 UUID of its object's; the tree is the IFC4 print's aggregation, containment, voids and nesting, and an occurrence inherits its type's node, and with it what its type carries; each class is named by its URI in bSDD's IFC 4.3 dictionary; properties are flat, `bsi::ifc::prop::<Name>` (the bridge's own sets `openeik::prop::<Name>`), so the names of their sets are lost; bodies are meshes of triangles and placements 4×4 matrices, in metres; and the version and the sentence that says the file is a print go on the project (`openeik::source`, `openeik::print`). Its fidelity report is the IFC4 print's, with what IFCX loses of it: the names of property sets, materials and their layers, which door or window fills an opening, zones, where a circuit starts, the map conversion and true north. The prints of `examples/one-storey-house` and `examples/l-house` (10 products and 6 types; 130 products and 18 types) compose and pass the schema validation of buildingSMART's reference library, from its IFC5 development repository, and read in IFClite.

### 1.1 Heating, ventilation and plumbing

How a bridge can export the systems, the insulation, the heat and the components of Part II, 6.9 to IFC4, as the reference bridge does.

**Systems.** One IfcDistributionSystem for each system and number the model's runs use, named by the system's code and number as the project's drawings write it (`VS20`), its long name the system's name, and its predefined type by the system:

| System | IfcDistributionSystemEnum |
|---|---|
| `coldWater` | DOMESTICCOLDWATER |
| `hotWater`, `hotWaterCirculation` | DOMESTICHOTWATER |
| `sewage` | DRAINAGE |
| `storm` | STORMWATER |
| `greaseWaste`, `oilWaste` | WASTEWATER |
| `heating`, `districtHeating` | HEATING |
| `brine` | CHILLEDWATER |
| `compressedAir` | COMPRESSEDAIR |
| `gas` | GAS |
| `supplyAir`, `outdoorAir` | VENTILATION |
| `extractAir`, `exhaustAir`, `kitchenExhaust` | EXHAUST |

Its runs and terminals are assigned to it by IfcRelAssignsToGroup: a run by its system and number; a component in a pipe or a duct by its run's; another terminal by the system and number of the runs joined to it, else, for a heating terminal of a project with one heating system, by that system's number, else by its own system under the number 1. A port's flow direction is SOURCEANDSINK on a heating or a cooling system.

**Components.** A component exports as the class and predefined type of its kind:

| Kind | IFC4: class, predefined type |
|---|---|
| `isolatingValve` | IfcValve, ISOLATING |
| `checkValve` | IfcValve, CHECK |
| `balancingValve` | IfcValve, DOUBLEREGULATING |
| `controlValve` | IfcValve, REGULATING |
| `safetyValve` | IfcValve, PRESSURERELIEF |
| `backflowPreventer` | IfcValve, DOUBLECHECK |
| `airVent` | IfcValve, AIRRELEASE |
| `pump` | IfcPump, CIRCULATOR |
| `strainer` | IfcFilter, STRAINER |
| `energyMeter` | IfcFlowMeter, ENERGYMETER |
| `expansionVessel` | IfcTank, EXPANSION |
| `damper` | IfcDamper, CONTROLDAMPER |
| `fireDamper` | IfcDamper, FIREDAMPER |
| `smokeDamper` | IfcDamper, SMOKEDAMPER |
| `flowRegulator` | IfcDamper, BALANCINGDAMPER |
| `silencer` | IfcDuctSilencer: ROUND in a round duct, RECTANGULAR in a rectangular one |
| `ductFan` | IfcFan, USERDEFINED, with the ObjectType `INLINE`: IFC4's IfcFanTypeEnum has no inline fan |

A unit heater exports as IfcSpaceHeater, CONVECTOR. In IFC a component in a pipe or a duct sits between two segments of its run, connected port to port (IfcRelConnectsPorts); a bridge MAY split the run at its components to write it so.

**Insulation and heat.** IFC holds a run's insulation as an IfcCovering of the predefined type INSULATION, related by IfcRelCoversBldgElements, or as properties. A code has no standard property: a bridge writes the code, its meaning where it knows it, and the thickness in a property set of its own. A space heater's power is `Pset_SpaceHeaterTypeCommon.OutputCapacity`; a terminal's design difference, kv and presetting go in a property set of the bridge's.

The reference bridge writes an IfcDistributionSystem per system and number (`writeMepSystems`), each run's insulation in a property set of its own (`Pset_StomraInsulation`: the code, its meaning and its thickness) and each terminal's heat in another (`Pset_StomraHeat`: its power, its flow in l/h, its design difference, kv and presetting), and the classes above (`terminalIfc`; a silencer's predefined type by its duct, `mepIfc`). It does not split a run at its components: one IfcPipeSegment or IfcDuctSegment runs through them.

Reference: `writeMepSystems`, `ifcSystemType` and `portDirection` in `src/core/io/ifcMepSystems.ts`; `terminalIfc` and `mepIfc` in `src/core/elements/mep.ts`.

*Note (informative).* Until Stomra 669 (live since 24 October 2026) the reference bridge put a terminal other than a component in its own system under the number 1, where its plans and its heat put a radiator on the number of the heating runs joined to it: a radiator of the system 20 was in IFC's VS1. It now writes it as this section says (open questions, section 2, row 32), and so does `ifcOfModel` of `@openeik/tools`.

*Note (informative).* The reference bridge writes the components in a duct since Stomra 676 (live since 24 October 2026, `main` `f02ba07e`). It puts a duct part that names no duct (Part II, 6.9) in the system and number of the duct it stands on, the nearest within 300 mm in plan, and writes a silencer there ROUND or RECTANGULAR by that duct, as it does for a component in a duct. It writes `INLINE` as the ElementType of a fan's IfcFanType too.

**Sources.** buildingSMART's documentation of IFC4 ADD2 TC1: [IfcDistributionSystem](https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcsharedbldgserviceelements/lexical/ifcdistributionsystem.htm), [IfcValve](https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcvalve.htm), [IfcPump](https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcpump.htm), [IfcFilter](https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcfilter.htm), [IfcFlowMeter](https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcflowmeter.htm), [IfcTank](https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifctank.htm), [IfcDamper](https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcdamper.htm), [IfcDuctSilencer](https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcductsilencer.htm), [IfcFan](https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcfan.htm), [IfcSpaceHeater](https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifchvacdomain/lexical/ifcspaceheater.htm) and [IfcCovering](https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/ifcproductextension/lexical/ifccovering.htm).

### 1.2 How a door opens and a window is divided

By the steward's decisions of 26 and 27 October 2026 (open questions, 71), from the operation Part II, 6.5 gives a door and a window. A bridge that writes IFC4 SHOULD write a door's `OperationType` (IfcDoorTypeOperationEnum) and a window's `PartitioningType` (IfcWindowTypePartitioningEnum) so: on its IfcDoorType or IfcWindowType where it has a type, leaving the IfcDoor's or IfcWindow's own unset, as buildingSMART's normative rules BLT001 and BLT002 require; and on the IfcDoor or IfcWindow itself where it has none. Where the doors or windows of one type record open or are divided in different ways, it SHOULD write an IFC type for each way, so that the occurrences of every IFC type share theirs. Left and right are as IFC4 sees them, looking the way the leaf opens ("as viewed in the direction of the positive y-axis"): towards the side Part II, 6.5, rule 2 names. Looking towards the wall's exterior, the opening's start is on the left; towards its interior, on the right.

| A door whose type's `openingFamily` is | OperationType |
|---|---|
| of the kind `door`, one leaf, swinging (`operation` `swing` or none) | SINGLE_SWING_LEFT where its hinge side (Part II, 6.5, rule 1) is on the left, else SINGLE_SWING_RIGHT |
| of the kind `door` or `garageSideHinged`, two leaves (`lightsX`, or `panels`), swinging | DOUBLE_DOOR_SINGLE_SWING |
| of the kind `door`, `slide`, or `pocket` with one leaf | SLIDING_TO_LEFT where its hinge side, the way it slides (rule 4), is on the left, else SLIDING_TO_RIGHT |
| of the kind `door`, `pocket` with two leaves or more | DOUBLE_DOOR_SLIDING |
| of the kind `garageSideHinged`, one panel | as a door of one leaf that swings |
| of the kind `garageRoller` | ROLLINGUP |
| of the kind `garageFolding` | DOUBLE_DOOR_FOLDING |
| of the kind `garageSectional` | USERDEFINED, its user-defined type `SECTIONAL` (IFC4 has none) |
| not `operable`; hinged at its `top` or `bottom` (Part II, 6.5, rule 6); swinging with three leaves or more; or no `openingFamily` | NOTDEFINED |

A window's partitioning is its grid of lights: across, an `openingFamily`'s `lightsX`, or one more than its `mullions.x`, or a `windowFamily`'s `panes`; up, `lightsY`, or one more than its `mullions.z`, or one more than its `transoms`. One light is SINGLE_PANEL; two or three side by side DOUBLE_PANEL_VERTICAL or TRIPLE_PANEL_VERTICAL; two or three one above the other DOUBLE_PANEL_HORIZONTAL or TRIPLE_PANEL_HORIZONTAL; any other grid USERDEFINED, its user-defined type `<across> x <up>`. A window whose type has neither family is NOTDEFINED. A type that a bridge does not split so writes the operation or partitioning all its occurrences share, else NOTDEFINED.

A door 900 wide with one leaf hinged `left`, swinging `inward`, on a wall that runs east: its hinges are at its west end, and it opens into the building, to the south; looking south, west is on the right, so it is SINGLE_SWING_RIGHT. With `handFlipped`, or swinging `outward`, it is SINGLE_SWING_LEFT. Sliding instead (`operation: "slide"`), it is SLIDING_TO_RIGHT.

*Note (informative).* BLT001 and BLT002 are rules of release 1.0.2 of buildingSMART's ifc-gherkin-rules, those its validation service runs. Until 27 October 2026 this section had a bridge write the operation on both the occurrence and its type, and a type whose occurrences differed say NOTDEFINED; the bridge of `@openeik/tools` writes it as above since that day, keying an IFC type of its own for each other way (`type:<class>:<type id>:<what it carries, as JSON>`).

Reference: `doorOperation` and `windowPartitioning` in `packages/openeik/src/ifc-ports.js` of `@openeik/tools`; the reference bridge's `doorOperation` in `src/core/io/ifcExport.ts`.

*Note (informative).* The reference bridge writes a door's operation and a window's partitioning with `doorOperation` and `windowPartitioning` of `@openeik/tools`, as this section says, since Stomra 727 step 4 (merged on 28 October 2026, `main` `bb4037f2`; open questions, section 2, row 36). Before, it wrote a door's operation type only where it slid, as SLIDING_TO_LEFT towards the opening's start and SLIDING_TO_RIGHT towards its end whichever side it opened to; NOTDEFINED otherwise, and on every type and window.

### 1.3 Shafts' openings

By the steward's decision of 26 October 2026 (open questions, 77). A bridge that writes IFC MUST write, in each host a shaft takes something out of (Part IV, 4.2, rule 8) and that the export writes, an IfcOpeningElement voiding it (IfcRelVoidsElement): of the predefined type OPENING where the shaft takes all of the host within its outline, and RECESS where it ends inside it, a foundation included. Its body is the shaft's outline extruded over the heights it takes out of the host. The host's own body has the shaft's prism cut already, as a wall's has its doors' and windows' openings (rule 1).

The reference bridge keys an opening `shaft:<shaft id>:<host id>` and its relation `void:shaft:<shaft id>:<host id>` for the GlobalIds of rule 3, names it by the shaft, and extrudes the outline from the higher of the shaft's base and a millimetre under the host's lowest point to the lower of its top and a millimetre over the host's highest point. `ifcOfModel` writes it so.

*Note (informative).* The reference bridge writes a recess and an opening in a foundation as this section says since Stomra 750 step 1 (live since 28 October 2026, `main` `5f5fc807`; open questions, section 2, row 35); before, it wrote a recess as an OPENING too, and none in a foundation. It also writes the shaft's purpose in a property set of its own.

### 1.4 Where a member's profile sits (informative)

By the steward's decision of 26 October 2026 (open questions, 78), an informative mapping in 0.1, as section 1.1's is: it asks no new body. A bridge that writes a member's profile, a beam's or a column's section as an IfcMaterialProfileSet on its type, writes on the member:

- an 'Axis' representation along its **reference line** (Part IV, 6.5), the line through the middle of its section, from its start to its end; and
- an IfcMaterialProfileSetUsage of its type's set with the **cardinal point 5**, the middle of the profile's bounding box, which IFC4 places on the member's axis line. A beam's roll and a column's `turn` turn the profile about that line (Part IV, 6.2, rule 5, and 6.1, rule 3), and a tapered beam's end section is an IfcMaterialProfileSetUsageTapering.

IFC4 places a parameterised profile by the middle of its bounding box, as the reference line runs through it. A roll turns the section about the reference line, so only the cardinal point 5 keeps a rolled beam in place: with 2, the bottom middle, the profile would turn about the beam's underside. An analysis export writes the member's axis instead, through its centroid (Part IV, 6.5), with the cardinal point 10. A beam built from a framing family has no single profile (Part IV, 6.5), and gets no usage.

**A rounded rectangle and an outline.** By the steward's decision of 27 October 2026 (open questions, 87), a bridge writes a `rectangle` with a `radius` (Part II, 5.2, rule 7) as an IfcRoundedRectangleProfileDef, its XDim the section's `width`, its YDim its `depth` and its RoundingRadius its `radius`, which is at most half the smaller of the two, as IFC4's rule ValidRadius asks too (a larger `radius` held from before is written as that half, as it is drawn); and one without a `radius`, or with 0, as an IfcRectangleProfileDef. It writes a `custom` section (rules 8 to 10) as an IfcArbitraryClosedProfileDef, or as an IfcArbitraryProfileDefWithVoids where it has voids, named by its `designation`, each contour an IfcIndexedPolyCurve over an IfcCartesianPointList2D of its own:

- each run of straight sides is an IfcLineIndex of its points, and each arc an IfcArcIndex of three: its start, a point on it and its end. A `through` entry's arc passes through that entry's point; a rounded corner's arc starts and ends where it meets its two sides, and passes through its own middle;
- the last segment ends on the first point, and no two points of a contour are one: where two rounded corners take the whole of the side between them, their arcs meet at one point;
- the points are the outline's less the middle of its bounding box, so that the profile's origin is its middle, as a parameterised profile's is, and the cardinal point 5 sets it on the reference line;
- the outline runs counter-clockwise and each void clockwise, a contour written the other way round reversed, and each starts at its first corner, or, where that corner is rounded, where its arc meets the side towards the entry before it.

Nothing of the section is lost: its corners and its arcs are IFC4's own, and only the origin of its points and the way round they were written, which place nothing (Part II, 5.2, rules 8 and 10), are not kept.

*Note (informative).* Neither the reference bridge nor `ifcOfModel` writes a usage or an 'Axis' representation yet: both write the profile on the type alone, the member's body faceted (section 1, its notes). Since 27 October 2026 `ifcOfModel` writes a `rectangle` with a `radius` and a `custom` section as this section says, on the type's IfcMaterialProfileSet; the member's faceted body draws each arc in four straight pieces a quarter turn, and its fidelity report says so. The reference bridge writes the IfcRoundedRectangleProfileDef since 27 October 2026 (main `d12a2c54`), and a `custom` section's IfcArbitraryClosedProfileDef or IfcArbitraryProfileDefWithVoids since 28 October 2026 (main `35bef7f5`), with the segments and points `ifcOfModel` writes.

### 1.5 Imported property sets

By the steward's decision of 28 October 2026 (open questions, 88). An element or a type imported from IFC carries the property sets it was imported with in `ifcSource.properties` (Part II, 6): by set and by property, each value a text, a number or a truth value. A bridge that prints them (section 1, rule 5) writes them so:

1. **Where.** An element's with the object it was imported as, where the export writes one: the opening of a door or a window imported as an IfcOpeningElement; otherwise with the object the export writes for the element, a room's space included. A type's with each IFC type the export writes for it, as that type's own sets (HasPropertySets), since IFC4 relates no property set to a type by an IfcRelDefinesByProperties.
2. **A set IFC4 defines.** A set whose name starts with `Pset_`, that IFC4's property set templates define, and whose template applies to the object, is written under its name. A template applies as buildingSMART's validation service holds it (its rule PSE001): to an occurrence of a class the template names or of a subclass of one, and to a type of such a class's type; where the template names a predefined type with the class, only to an object of that predefined type, its own, else its type's (for USERDEFINED, its object type or its element type); a set driven by types only to no occurrence, and a set driven by occurrences or by performance to no type.
3. **Typed as the template types it.** In such a set, each property the template defines is written as the template has it: a single value of the measure or the defined type the template names; an enumerated value of the enumeration it names, the value one of its values; a bounded value whose set point is the value; a list value of the one value. A text is typed only where the type is a text and the text fits it (an IfcLabel or an IfcIdentifier of at most 255 characters; an IfcDate, an IfcDateTime, an IfcTime or an IfcDuration in its ISO 8601 form), a truth value only as an IfcBoolean or an IfcLogical, and a number only as a number within its type's bounds (an IfcPositiveLengthMeasure more than 0, an IfcNormalisedRatioMeasure from 0 to 1, an IfcInteger whole). Where the record's `ifcSource.units` is `"specification"` (Part II, 6), it holds a number in this specification's units, a length in millimetres and an angle in degrees (Part I, 3), any other measure in its SI unit; the bridge writes it in the units of its print, and declares the unit of each measure it writes (IfcUnitAssignment). Where `units` is absent, the record does not say which units its numbers are in: a number the template types as a measure with a unit, such as a length, an angle, an area, a volume or a thermal transmittance, cannot be typed, and rule 4 writes it, as it was imported; a number of a measure without a unit, such as a count, a ratio, an IfcInteger or an IfcReal, is typed as this rule says. No bridge converts it.
4. **Everything else, under a name without `Pset_`.** A set IFC4 does not define, or whose template does not apply to the object; a property the set's template does not define; and a value that cannot be typed as rule 3 says (a text where the template names a measure, a number of a measure with a unit where the record does not say its units (rule 3), a value its enumeration does not hold, a property the template makes a reference, a table or a complex property, which one value is not, or one whose type the templates do not give) are written in a set whose name does not start with `Pset_`. Its name is the set's name without `Pset_`, and a set not named `Pset_` keeps its name, unless that name is empty, starts with `Pset` in any case of its letters, starts with `OpenEik_` (the bridge's own sets) or `Imported_`, is the name of a set the bridge writes itself on the object, or is the name another of the record's sets takes by this rule; then its name is `Imported_` followed by the set's whole name. In it, a text is an IfcLabel (an IfcText where it is longer than 255 characters), a number an IfcReal and a truth value an IfcBoolean.
5. **The bridge's own sets.** Where the bridge writes a set of the same name on the object itself (the sets of section 1.1 and of Part XIII, 10, a space's `Pset_SpaceCommon`, a railing's `Pset_RailingCommon` of section 1, rule 6), the imported properties join it, and a property the bridge writes itself keeps its value: the imported one is left out.
6. **The report.** The fidelity report (section 1, rule 2) says which imported properties are written outside their set, and why, and which are left out.

Why: an IFC file from an authoring tool holds sets and properties IFC4 does not define, such as a set `Pset_QuantityTakeOff`, or a property `TotalArea` in `Pset_RoofCommon`, and values of other types than IFC4's templates give. buildingSMART's rule PSE001 refuses a set named `Pset_` that IFC4 does not define, a property its template lacks and a value of another type; IFC reserves the prefix to the sets it defines, and its rule PSE002 warns of any other name that starts with `Pset` in any case. Printed as it was imported, such a record fails that rule; left out, its properties are lost. The name without `Pset_` keeps them readable, and `Imported_` keeps two sets apart and the bridge's own names its own.

*Note (informative).* `ifcOfModel` of `@openeik/tools` prints them so since 28 October 2026 (`packages/openeik/src/ifc-properties.js`). It reads IFC4's templates from the table buildingSMART's validation service holds a set against, `features/resources/IFC4/pset_definitions.csv` of its ifc-gherkin-rules, release 1.0.2 (`src/ifc4-psets.js`, under the MIT License), so that what it writes under a `Pset_` name is what PSE001 accepts. Where that table gives a property no data type, or an enumeration no values, as it does for `Pset_RoofCommon.LoadBearing` and for the `Status` of several sets, it takes the value to be one it cannot type (rule 4). It declares the SI unit of each measure it types that its print has no unit for, derived ones such as W/(m²·K) among them; it keys an imported set `source-pset:<key>:<set name>` for the GlobalIds of rule 3, as the reference bridge does, the key an element's id, `opening:<id>` or `room:<id>` for an opening's or a space's set, and `type:<the IFC type's key>` for a type's; and it writes the sets, and the properties in each, in the code-unit order of their names, so that a record prints to the same bytes whatever the order of its members. Its prints of records with imported sets pass IfcOpenShell 0.8.5's validation and buildingSMART's rules with no issue; PSE001 stops on an error of its own on a print that holds `Pset_StairCommon` or `Pset_ElectricalDeviceCommon` (section 1, the note on the suite), so the package's tests hold every set of its prints to the table as PSE001 does, and with PSE001's table read as Python reads it, those prints pass it too. The reference bridge writes an element's and an opening's imported sets under the names they were imported with, a text an IfcLabel, a number an IfcReal and a truth value an IfcBoolean, and a space's and a type's not at all (Conformance, 3). Since 29 October 2026 `ifcOfModel` types a number of a measure with a unit only where `ifcSource.units` is `"specification"`, and the package's entry exports what it prints the sets with, `importedSets` (which reads `units`), `propertyStep`, `enumerationStep`, `ifcClassName`, `SUPERTYPES`, `MEASURES`, `SI_UNITS` and `IMPORTED_PREFIX`, so that an application's own IFC writer prints them to the same bytes.

### 1.6 A framing family's parts (proposed for 0.2)

*Proposed for 0.2, with the proposal `framing` of Part IV, 6.6, and informative in 0.1. A bridge writes these only when it is asked for the proposal.*

A beam or a slab edge element whose type is a framing family has the bodies of its family's parts (Part IV, 6.6). A bridge asked for the proposal SHOULD write them so:

1. **A family of profiles.** Where every part that has a piece is a `profile` part, its parts are the element's own body: each part an IfcShapeAspect of its shape, named by the part's id, and a constituent of its IfcMaterialConstituentSet, as a door's parts are (Part IV, 4.3.1).
2. **A family with members.** Where a `member` part or a plate has a piece, each part that has one is an element of its own (Part IV, 6.6, rule 13), aggregated under the element's object by an IfcRelAggregates, placed in the element's placement, and with its body and its material: its type's material, and a nail plate's steel. The element's object has no body of its own: its parts carry it. Each part's GlobalId is derived from `framing:<element id>:<part id>`, and its Tag is its key; where two elements' ids and their parts' ids make one key, as an element `a` with a part `b:c` and an element `a:b` with a part `c`, a bridge refuses to print the model, since their GlobalIds would be one; its IFC type is one for each class, predefined type, user type and section, keyed `type:<class>:framing:<predefined type>:<user type>:<section>` and named by its user type, else its predefined type, and its section (`CHORD 45 × 220`), as the reference bridge keys and names its framing members' types. The element keeps its own class, its type and its GlobalId.
3. **Its class.** A part's IFC4 class and predefined type follow its role, as the reference implementation types a truss's members, and its name says its role in words, as the reference implementation names them (Part IV, 6.6, rule 13):

| A part | IFC4 class | Predefined type, and its user type | In words |
|---|---|---|---|
| a `member` of the role `topChord` | IfcMember | CHORD | top chord |
| `bottomChord` | IfcMember | CHORD | bottom chord |
| `web` | IfcMember | STRUT | web |
| `heelPost` | IfcMember | POST | heel post |
| `strut` | IfcMember | POST | strut |
| `collar` | IfcMember | COLLAR | collar tie |
| `rafter` | IfcMember | RAFTER | rafter |
| `purlin` | IfcMember | PURLIN | purlin |
| `header` | IfcMember | USERDEFINED, HEADER | header |
| `ridgeBeam` | IfcBeam | BEAM, RIDGEBEAM | ridge beam |
| `post` | IfcColumn | COLUMN | post |
| `plate`, and a `plate` part | IfcPlate | USERDEFINED, NAILPLATE | nail plate |
| a `profile` part, in a family with members | IfcMember | MEMBER | (its id alone) |

Why parts and not shape aspects: an IfcShapeAspect names a part of one object's shape that has no identity of its own, as a door's lining and its panel, and carries no class, predefined type, type, material profile or quantities of its own. A truss's chords, posts and webs are each a piece of timber that is cut, listed, joined and analysed apart. The steward decided on 21 October 2026 that an analysis takes each `member` part of a family as a member with its own axis (open questions, 10), and on 26 October 2026 that each is written as an IfcMember or an IfcBeam with its own body (open questions, 83). IFC4 writes such a frame as a whole decomposed into its parts by IfcRelAggregates, as its IfcElementAssembly of the predefined type TRUSS is: each part is placed relative to its whole, as buildingSMART's rule OJP001 asks, and contained in no storey, its whole being; and a whole whose parts carry its body has none of its own, as an IfcStair whose flights carry it, so that nothing is counted twice. A family of profiles, a rafter cut to its seat, is one piece, the element itself, and stays one object. The element keeps the class its category gives it, an IfcBeam for a beam, so that its type and its GlobalId stay those it has without the proposal.

*Note (informative).* The reference bridge writes the trusses and the wall framing its engine lays out (open questions, 83, item 2) as rule 2 does, a truss as an IfcElementAssembly TRUSS with its members under it, and a beam of a framing family as one IfcBeam, its parts all in its body. `ifcOfModel` of `@openeik/tools` writes them as these rules say since 29 October 2026 (`partClass` in `packages/openeik/src/compile-framing.js`). Its prints of the reference implementation's villa and its guesthouse with the proposal pass IfcOpenShell's validation and buildingSMART's rules, release 1.0.2, with no issue: the guesthouse's 78 trusses and rafters as 78 IfcBeams without a body of their own and 326 IfcMembers under them, the villa's 117 rafters as IfcBeams with their bodies, a shape aspect each part.

### 1.7 Where the plan stands on a map (proposed for 0.2)

*Proposed for 0.2 with Part II, 2.2 (the proposal `site`), and informative in 0.1, which names no coordinate reference system: a bridge writes a map conversion only where its caller names a system.*

By the steward's decision of 26 October 2026 (open questions, 79). A bridge that writes IFC, and reads a site whose `crs` names a horizontal system it projects (Part II, 2.2, rules 3 and 5), writes where the plan stands as an IfcMapConversion from its model's representation context to an IfcProjectedCRS:

1. **The system.** The IfcProjectedCRS's `Name` is `crs.horizontal` (`EPSG:3006`), its `VerticalDatum` `crs.vertical` where the site gives it, and its `MapUnit` the system's unit of length.
2. **The place.** The IfcMapConversion's `Eastings` and `Northings` are the site's latitude and longitude projected into the system, and its `OrthogonalHeight` the project's `elevationOffset`, each in the system's unit; its `XAxisAbscissa` and `XAxisOrdinate` are the direction of the plan's x axis in the grid, the plan turned by true north and by the grid's convergence at the site; and its `Scale` turns the model's millimetres into the system's unit, 0.001 for metres.
3. **What it cannot project.** A bridge that does not project the system writes no map conversion, and says so in its fidelity report (rule 2). A site without `crs` places the plan on no map, unless the bridge's caller names a system.

*Note (informative).* `ifcOfModel` of `@openeik/tools` writes it so with the proposal `site`, for the transverse Mercator systems it projects already: SWEREF 99 TM and its twelve local zones (EPSG:3006 to 3018), and the UTM zones of ETRS89 (EPSG:25828 to 25838) and of WGS 84 north of the equator (EPSG:32601 to 32660), by Krüger's series (`src/ifc-geo.js`). A system its caller names (`crs`) goes before the site's.

### 1.8 Classification references (proposed for 0.2)

*Proposed for 0.2 with Part II, 11 (the proposal `classifications`), and informative in 0.1, which defines no classification reference.*

By the steward's decisions of 11 and 27 October 2026 (open questions, 4 and 86). A bridge that writes IFC writes the classification references of the records it prints objects for (Part II, 11, rule 6):

1. **The system.** One IfcClassification for each system and edition the print's references name: its `Name` the `system`, its `Edition` the `edition` where the references give one.
2. **The reference.** One IfcClassificationReference for each reference, the same `system`, `edition`, `code` and `uri` being one reference: its `Identification` the `code`, its `Location` the `uri` where it gives one, and its `ReferencedSource` its system's IfcClassification.
3. **What it classifies.** An IfcRelAssociatesClassification relates each reference to every object of the print whose record carries it: an element's product, or the IfcSpace of a room; each IFC type a type is written as; the project's IfcProject; a level's IfcBuildingStorey.
4. **What it leaves out.** The references of a record the print writes no object for are not printed, and the fidelity report says how many and of which records (rule 2): a view or a grid, say, a type no element of the print uses, and a material. IFC4 relates a classification to a material only by a relationship that no rooted entity references, which buildingSMART's rule IFC105 refuses, so a material's references stay in the model.

A relation's GlobalId is derived from the project's id, as rule 3 derives every GlobalId, and the key `classification:` followed by the canonical JSON (Part III, 6) of the list `[system, edition, code, uri]`, `null` for an `edition` or a `uri` the reference does not give: `classification:["NL-SfB","2005","21.21",null]`.

### 1.9 A member between two points, and a slab on a sloped plane (proposed for 0.2)

*Proposed for 0.2 with Part II, 6.2.1 and 6.3.1 (the proposals `member` and `slopedSlab`), and informative in 0.1, which has neither. A bridge writes these only when it is asked for the proposal.*

By the steward's decisions of 30 October 2026 (open questions, 110 and 111). A bridge that writes IFC writes:

1. **A member** (Part II, 6.2.1) as an IfcMember of the predefined type NOTDEFINED, typed by an IfcMemberType, NOTDEFINED, as it writes a beam as an IfcBeam and a column as an IfcColumn: its body as Part IV, 6.7 builds it, its type's section an IfcMaterialProfileSet on its IfcMemberType as a beam's and a column's are (section 1, its notes), and its GlobalId and its type's derived as rule 3 of section 1 derives a beam's (`<project id>:element:<id>`, and `type:IFCMEMBER:<type id>`). Section 1.4's mapping places its profile on its reference line as it places a beam's, with the cardinal point 5. What it says of a brace, a strut or a tie, a reader takes from its type's name; its record says no more (Part II, 6.2.1, rule 7).
2. **A slab on a sloped plane** (Part II, 6.3.1) as it writes a level slab, an IfcSlab of its category's predefined type with its type's IfcMaterialLayerSet, its body each layer's as Part IV, 4.2.1 builds it: the outline between two parallel planes, its sides plumb, written as a pitched roof's layers are. Its openings are a level slab's (section 1.3).

*Note (informative).* `ifcOfModel` of `@openeik/tools` writes them so with the proposals `member` and `slopedSlab`: a member's body faceted, as a beam's is, and each layer of a sloped slab a closed face set between its two planes. Its print of `examples/l-house` with seven members (a brace, a rolled angle, a plumb tube, a post, two rafters mitred, one of them tapered, and a tie butting one) and two sloped slabs (one by an edge, with a void, one by a fall square to none of its edges) passes IfcOpenShell's validation and buildingSMART's normative rules, release 1.0.2, with no issue, checked on 30 October 2026. The reference bridge has neither (Part II, 6.2.1 and 6.3.1, their notes).

### 1.10 A provision for a void (proposed for 0.2)

*Proposed for 0.2 with Part II, 6.10 (the proposal `provision`), and informative in 0.1, which has no category `provision`.*

By the steward's decision of 30 October 2026 (open questions, section 4, D9). A bridge that writes IFC4 writes each provision ([Part II, 6.10](https://openeik.org/spec/0.1/language#6.10)) it prints:

1. **The object.** An IfcBuildingElementProxy of the predefined type PROVISIONFORVOID, in its level's storey, its body the provision's (Part II, 6.10, rule 2). Where it has a type, its IfcBuildingElementProxyType carries the predefined type, since IFC4 has a type carry what its occurrences share (buildingSMART's rule OJT001).
2. **Its properties.** IFC4's `Pset_BuildingElementProxyProvisionForVoid`, which IFC4 applies to an IfcBuildingElementProxy of that predefined type: `Shape`, an IfcLabel, `Rectangle` or `Round`; `Width` and `Height`, its `width` and `height`, for a rectangle, or `Diameter`, its `width`, for a round one, and `Depth`, its `provision.depth`, each an IfcPositiveLengthMeasure; and `System`, an IfcLabel, the names of the runs its `provision.forIds` names, in that order, joined by `, `, where it names any of the model's.
3. **Nothing else.** It voids no element: an IfcOpeningElement is written for what its host's records cut (section 1, rule 1), never for a provision. Its answer is not printed.

*Note (informative).* IFC 2x3 named the set `Pset_ProvisionForVoid`; IFC4 renamed it, and a print of IFC4 that writes the old name fails buildingSMART's rule PSE001, which refuses a set named `Pset_` that IFC4 does not define. The reference implementation writes `Pset_ProvisionForVoid` (open questions, section 2). `ifcOfModel` of `@openeik/tools` writes rules 1 to 3 with the proposal (`provisionProperties` in `packages/openeik/src/provision.js`).

## 2. Reading IFC (proposed for 0.2)

*Proposed for 0.2, and informative in 0.1: it adds no rule to 0.1, and no file of 0.1 is held to it. It is the proposal `ifc-read`, by the steward's decision of 29 October 2026 (open questions, 95).*

A reader of IFC starts an OpenEik project from an IFC file: the bridge of section 1, the other way. It is deterministic, with no language model and no judgement of its own in it: the same file gives the same records and the same bodies. It reads a file once, to start a project. It is no live link to the file, and it tracks no revisions between deliveries of it: a later delivery read again is another start. What a record of Part II cannot say of an element without recognising it, its shape, the reader keeps as the file draws it, a body given beside the records; an assistant, or a person, reviews the project afterwards and upgrades it, rewriting each element it recognises in its category's own members (section 2.6).

This first version recognises no element. Every element's shape is a given body, and its record says so (section 2.3).

### 2.1 What a reader writes

1. **The project.** Its id is the IfcProject's GlobalId read as section 2.4, rule 1 says; its name the project's `Name`, else its `LongName`; its units `mm` and its up axis `Z`; its `elevationOffset` the first IfcSite's `RefElevation`, where the file gives one. Its `site` is left out: in 0.1 a site gives its latitude, its longitude, true north and a time zone together (Part II, 2), and IFC gives no time zone.
2. **Levels.** Each IfcBuildingStorey is a level, named by its `Name`, else its `LongName`, at the height its placement puts it at in the file's world, else at its `Elevation`. A file without a storey gives one level, `level-0`, at 0.
3. **Elements.** Each element of the file, an IfcElement that is no feature element (an opening, a projection) and no virtual element, and each IfcSpace, is an element of the category the table below gives its class: the first row whose class it is, or is a subclass of, and whose predefined types, where the row names them, hold its own, else its type's. IfcBuildingElementProxy, and every class the table does not name, is `generic` (Part II, 6.6), and its type's `generic` names its class (`ifcClass`), what it stands for (the IFC type's `ElementType`, else its `Name`, else the class's name in words) and its discipline: `systems` for a distribution element, `site` for a geographic or a civil element, `structure` for a member, a plate, a pile, a fastener and reinforcement.
4. **Types.** Each type of a product (an IfcTypeProduct, IFC2X3's door and window styles among them) is a type: of the category its elements are read as, else of the category its class's occurrences are; one type for each category, and for `generic` each class, where its elements are read as more than one. The elements of a category and a class that no type types get a type of the reader's own. A type's `name` is its `Name`, else its `ElementType`, else its class; its `material` as section 2.2, rule 3 says; its `color` its material's, else the first colour of its representation's or its first element's body, else `#c8c8c8`; its `width` and `height` its first element's (section 2.3, rule 2), else those of the box round its representation, else 1 mm.
5. **An element's level** is the level of the storey that contains it (IfcRelContainedInSpatialStructure), directly, through a space or through the element it is a part of (IfcRelAggregates, IfcRelNests); else the highest level at or under its body's lowest point; else the lowest level.
6. **A door or a window** is hosted (`hostId`) by the element whose opening it fills (IfcRelFillsElement and IfcRelVoidsElement), where that element is read as a wall. One that fills no wall's opening is `generic`, since Part II, 6.1, rule 5 hosts a door and a window by a wall.
7. **A room's** `room.number` is its space's `Name`, else its `LongName`, else its id, and its `room.name` its `LongName`.

| IFC4 class | Predefined type | Category |
|---|---|---|
| IfcSpace | | `room` |
| IfcWall | | `wall` |
| IfcSlab | ROOF | `roof` |
| IfcSlab | any other | `slab` |
| IfcRoof | | `roof` |
| IfcFooting | | `foundation` |
| IfcCovering | CEILING | `ceiling` |
| IfcBeam | | `beam` |
| IfcColumn | | `column` |
| IfcDoor | | `door`, where it fills a wall's opening (rule 6) |
| IfcWindow | | `window`, likewise |
| IfcRailing | | `railing` |
| IfcPipeSegment | any but GUTTER | `pipe` |
| IfcDuctSegment | | `duct` |
| IfcFurnishingElement | | `furniture` |
| every other element, IfcBuildingElementProxy among them | | `generic` |

The table is the bridge's classes (section 1) read the other way, for the categories whose records a reader can write from what a file says without recognising the element. A gutter, a stair, a component, a terminal, reinforcement and a slab edge element need members of their category's own, such as a gutter's roof edge or a stair's shape, which only recognising the element gives: until a rule does (section 2.6), they are `generic`, and their class and predefined type are kept (section 2.2, rule 1).

A reader leaves out, and its report says so: an opening, which OpenEik derives from what fills it (Part IV, 4.3), or which is in its host's body where nothing fills it; ports, systems, zones, grids and annotations; and the GlobalIds of the project, its site, its building and its storeys, which 0.1 holds for none of them.

### 2.2 What it keeps

1. **What each object was.** An element and a type carry `ifcSource` (Part II, 6): the object's `globalId`; its class, `entity`, as IFC writes it (`IfcWallStandardCase`); and, proposed with this section, its `predefinedType` (an occurrence's own, unless that is unset or NOTDEFINED and its type's is neither, then its type's; a type's own) and, where that is USERDEFINED, its `objectType` (an occurrence's ObjectType, a type's ElementType).
2. **Its property sets**, in `ifcSource.properties`, with `units: "specification"` (Part II, 6): each property set (IfcPropertySet) related to an occurrence, and each a type holds; each property's value a text, a number or a truth value: a single value's; an enumerated value's or a list value's one value; a bounded value's set point, else its one bound. A number is converted from the units of the file (its IfcUnitAssignment), or of the property where it gives its own: a length into millimetres, a plane angle into degrees, any other measure into its SI unit; an SI unit's prefix applied, squared for an area and cubed for a volume; a conversion-based unit by its factor (a foot, a degree, a square foot); a derived unit by its elements' factors raised to their exponents; a temperature in degrees Celsius into kelvins. A number of a measure without a unit (IfcReal, IfcInteger, IfcCountMeasure, a ratio) is kept as it is. Left out, and said so: a quantity set (IfcElementQuantity), which is derived data (Part II, 10); a set of the bridge's own (`OpenEik_`, section 1.5, rule 4); a value of several values, a range, a table, a reference or a complex property; and a value in a unit the reader does not convert.
3. **Materials, by name.** A type's `material` is the name of the material the file associates with the type, else with its first element: a material's name; a layer set's, a profile set's or a constituent set's name, else its first material's; a usage's, its set's. Each such name of at most 80 characters is a material record (Part II, 4), of the colour the file gives the material, else `#c8c8c8`, and the pattern `none`; a longer one is its types' material text alone (Part II, 4, rule 2). A layer set's layers are not read.

### 2.3 Shapes

1. **A given body.** A product's shape is a body given in the derived layer (Part IV, 1.1.1, the proposal `derived`), keyed `elements/<id>`, and never a record's own geometry: the element's record carries `shape: "given"`, proposed with this section. A result names its element by its hash, and its type too where it places the type's representation, so that a reader uses it only while they hold (Part IV, 1.1, rule 2); its producer is the reader, and its rule `ifc-read`, else `ifc-read, mesher` (rule 5). A type's representation that its elements place is a result of its own, keyed `types/<id>`, and each of those elements' body `mapped` onto it.
2. **The members that place it.** An element whose shape is given still carries the members its category's schema asks for. They place it, and say nothing of its shape: `levelId`, its level (section 2.1, rule 5); `offset`, the height of its placement's origin over that level; `profile`, for a column, a room, a piece of furniture, a door and a window, its placement's origin in plan; for a slab, a roof, a ceiling and a foundation, the corners of the box round its body in plan, counter-clockwise from its least x and y, at least 10 mm a side (a square of 1000 mm from its origin where it has no body); for every other category, its origin and the point 1000 mm from it along its placement's x axis in plan (along +x where that axis is upright); `height`, the height of the box round its body, and `width`, the shorter of its sides in plan, else the longer; a door's and a window's their `OverallHeight` and `OverallWidth`, where the file gives them; each at least 1 mm and at most 100 000 mm, and 1 mm where it has no body.
3. **What a reader reads.** A reader SHOULD read these items of a product's 'Body' representation (else of its 'Facetation'), in the file's length unit made millimetres, each placed by its IfcLocalPlacement chain and by IfcMappedItem, a transformation's scale and mirror included: an IfcExtrudedAreaSolid of a rectangle, a rounded rectangle, a circle, a hollow circle or rectangle, an ellipse, an I, L, U, T or C shape, an arbitrary closed profile with voids or without (a polyline, an indexed poly curve, a composite curve of polylines and of trimmed lines and circles), or a composite, derived or mirrored profile of them; an IfcPolygonalFaceSet and an IfcTriangulatedFaceSet; an IfcFacetedBrep, with voids or without; an IfcShellBasedSurfaceModel and an IfcFaceBasedSurfaceModel. It writes an upright extrusion as an `extrusion`, a circle's as a `cylinder`, a circle swept square to itself as a `rod`, a body that is one IfcMappedItem of its type's first 'Body' representation map, placed with no scale and no mirror, as `mapped`, and every other as `faces`. It draws an arc in straight pieces, four a quarter turn, as section 1.4's note draws a section's; a circle that is no cylinder or rod as a polygon of 32 sides; and a parameterised profile as Part II, 5.2 draws its section, an I's, an L's, a U's and a T's root fillets as arcs and their edge radii and slopes not at all: and says each is approximated.
4. **Booleans and voids.** A file whose header names a Reference View (`ViewDefinition [ReferenceView…]`) has its openings cut in its bodies, which a reader takes as they are. In any other, an element that openings void (IfcRelVoidsElement), and one whose shape is a boolean result, needs a cut that a reader without a solid modeller does not make: it is the mesher's (rule 5); without one, a reader writes a difference as its first operand, a union as its two operands and a voided body as the file draws it before the cut, and says each is approximated. An intersection it does not read (rule 5).
5. **What it does not read** it leaves to a mesher, where it has one: a tool apart that meshes the products of a file, with a solid modeller, by their GlobalIds. It asks the mesher once, for the products rules 3 and 4 leave, and writes each mesh as `faces`, in triangles. A product that neither reads is kept without a body, and the report names it, and why. A reader never guesses a shape.
6. **With the proposal.** A tool that applies the proposal reads an element whose `shape` is `"given"` so:
   - a compiler derives no body from its members and reports it; it cuts nothing with it, a door or a window in its wall nor a shaft in its hosts, and nothing in it;
   - a validator holds `shape`, `ifcSource.predefinedType` and `ifcSource.objectType` to their schemas (`schemas/proposed/ifc-read/`), and the element to its category's schema, its references and its type's category, and not to the rules a validator checks in code that place a body or size one: Part II, 5.3; 6.1, rules 2, 3, 5 (but that a door or a window is hosted by a wall), 6, 8, 11 and 13; 6.4; 6.6, rules 2 and 3; 6.7; and 12;
   - a bridge prints its given body, as the class and the predefined type it was read with (`ifcSource.entity` and `predefinedType`, and `objectType` for USERDEFINED), where that is an element class it writes, its IFC type's predefined type its type record's; and for a door or a window no opening.

   A tool of 0.1 keeps `shape` and the two members of `ifcSource` as members it does not know (Part I, 10), and derives bodies from the members of rule 2, which are no shape. A project read from IFC is read with the proposal until its elements are recognised (section 2.6).

### 2.4 Determinism

1. **Ids.** An element's, a type's and a level's id is its object's GlobalId read as the UUID its 128 bits encode, in lower case with its hyphens, as buildingSMART's converter keeps an object's identity (`2JUHrTM_j3UxZiBnyBfByx` is `93791d5d-5beb-437b-b8ec-2f1f0ba4bf3b`), so that no two ids differ by the case of their letters alone and no two of their files fold to one (Part III, 4.3); the project's id likewise. A GlobalId that an object read before it holds (the storeys first, then the elements and spaces, then the types, each by its instance number) gives its id `-2`, `-3` … after it, and its `ifcSource` keeps the GlobalId, which a print gives the first (section 1, the note on rule 3); a text that is no GlobalId gives `ifc-` and the first 32 hexadecimal digits of its SHA-256. A type that is one of several of one IFC type (section 2.1, rule 4) has `-` and its category after its id, `-generic-` and its class in lower case for a generic one; a type of the reader's own is `ifc-` and its category (`ifc-generic-` and its class in lower case); a material's id is `m-` and the first 16 hexadecimal digits of the SHA-256 of its name.
2. **Order.** Levels by elevation, then by id; materials, types and elements by id, and results by key, in code-unit order; property sets and their properties by name; a result's bodies in the order of the representation's items.
3. **No clock.** Nothing of the time of reading is in the records, the bodies or the report, and coordinates are millimetres rounded to a millionth: one file read twice gives the same bytes.

### 2.5 The fidelity report

A reader MUST publish with what it reads a fidelity report, as a bridge publishes one with its print (section 1, rule 2): the schema and the view the file names, and its length and angle units; the counts of the levels, materials, types and elements, by category; each class of product the file holds, how many, what they are read as, and how many have a body the reader read, a body a mesher gave, and none; the property sets and values kept; and what it approximated (section 2.3, rules 3 and 4; a level an element is put on by its height; a type split, or of its own), what it left out (sections 2.1 and 2.2) and what it kept without a body (section 2.3, rule 5), each with its reason and the GlobalIds of the objects it concerns.

### 2.6 Recognising elements (later)

Recognising an element is writing its record in its category's own members, so that Part IV derives its body, and dropping its given body and `shape`. This first version recognises none. Each class's recognition is a rule of its own, held to the bridge's prints and the reference implementation's: a recognised element read back from a print derives the body the print drew. They are to be proposed class by class, in the order the bridge's classes were compared with the reference implementation's (section 1, its notes):

1. **Beams.** An IfcBeam extruded along its axis from a parameterised profile: a beam of a type with that section (Part II, 5.2), its line from its 'Axis' representation and its profile's place from its IfcMaterialProfileSetUsage (section 1.4).
2. **Columns.** An IfcColumn, likewise, upright.
3. **Footings.** An IfcFooting extruded upright from an outline, or from a circle as a round footing.
4. **Slabs.** An IfcSlab extruded upright from an outline with its voids, its type's layers its material layer set's.
5. **Walls.** An IfcWall's 'Axis' representation as its baseline, its material layer set usage as its type's layers and their offset, and the doors and windows that fill its openings as its hosted openings, which then cut it (Part IV, 4.3).
6. **Roofs.** An IfcRoof's, and an IfcSlab ROOF's, planes as a roof's edges and slopes (Part IV, 4.4).
7. **Railings.** An IfcRailing's line as a railing's on a line of its own, or along the edge it stands on (Part II, 12.2).
8. **Electrical devices.** A device's class and place as an element of the proposal electrical (Part XIII, 10, read the other way).

Until then, an assistant recognises an element as it writes any element, by commands (Part V); with the proposal, an element it rewrites leaves `shape` out.

*Note (informative).* `@openeik/tools` reads IFC so since 29 October 2026 (`eikOfIfc(text, {mesher})` in `packages/openeik/src/ifc-read.js`), and its compiler, validator and bridge apply the proposal when asked for it (`proposals: ["ifc-read"]`). It reads IFC4 and IFC2X3: an IFC2X3 occurrence's predefined type where that schema gives one where IFC4 does (a slab's, a covering's, a railing's, a footing's, a roof's, a stair's, a ramp's, a pile's and a transport element's), the rest as IFC4's. A later schema it refuses. Its mesher is an async function of the file's text that answers a Map from each GlobalId to meshes, `{positions, indices, color?, part?}`, positions in millimetres in the file's world, three a point, and triangles of three indices; a separate package builds one on web-ifc. Read back and printed again with `ifcOfModel(model, {bodies, proposals: ["ifc-read"]})`, the prints of `examples/one-storey-house` and `examples/l-house` give every element and every type again, 10 and 6, 130 and 18, each with the same GlobalId, class, predefined type and property sets, and each body the volume the compiler gives its element, a gutter's within 1 %, since the print draws its half-round in chords; the second print differs only where 0.1 holds no GlobalId: the project, its site, building and storeys, derived again from the new project's id, and the openings, 3 and 13. Both second prints pass IfcOpenShell 0.8.5's validation and buildingSMART's rules with no issue (section 1, the note on the suite). On the reference implementation's prints of its seven examples, with its timber framing and without (from 73 to 731 elements each), every element is read, each with a body but two stairs that its print gives none, each a whole of flights; printed again, every element and type comes back with its GlobalId, class and predefined type, and its property sets as section 1.5 prints imported sets: an IfcText as an IfcLabel, an IfcReal a template types as a measure as that measure, a bounded value by its set point, a set IFC4 does not define under its name without `Pset_`. The ports, systems, circuits and zones of its installations are not read.

## 3. Other bridges

| Bridge | Direction | In the reference implementation |
|---|---|---|
| IFC | import | as a linked reference model, not as native records; `@openeik/tools` reads IFC into records, proposed for 0.2 (section 2) |
| glTF (binary `.glb`), STEP, STL | export | 3D files for viewers and other tools, from the derived bodies (`model_export`, `src/core/io/modelExport.ts`) |
| DXF, PDF, SVG | export | drawings and sheets, from views (application features) |

0.1 specifies none of them.
