Part I. Foundations
1. Scope
OpenEik specifies:
- A language of records that state a building's design intent: a project, its levels, materials, types with layers, elements of thirty-one categories, and views (Part II).
- A code base: how a model is stored as a folder of files, one record per file, in a canonical text form (Part III).
- What is derived: which results a compiler computes from the records and which are never stored, and the geometric rules that 0.1 fixes (Part IV).
- Commands: the typed, validated, all-or-nothing writes that change a model (Part V).
- History: content-addressed versions, diffs and three-way merges by record and field (Part VI).
- An agent interface: how an AI agent reads, searches and changes a model (Part VII).
- Bridges: IFC printed from a version of a model (Part VIII).
- Electrical installations: devices and boards, circuits, control, cable carriers and modelled cables (Part XIII).
- Drawings: sheets, and drawings issued, reviewed and signed (Part XIV).
- Assets: binary files kept by their digests (Part XV).
- Ownership: the parties of a project, the paths each owns and the approvals a change needs (Part XVI).
- Commands with an envelope: who sends a batch, its base and mode, and refusals with codes (Part XVII).
- Review and issue: review rounds on a version, issues, receipts and prints that name their version (Part XVIII).
OpenEik is an open, non-profit standard. It does not require any service, product or host: a conforming folder may live on a local disk, in any git host or in any object store. Stomra's engine and its ek command are the reference implementation; they show that every rule here can be met, and they are the source of the conformance examples. Nothing in this text requires them.
1.1 What 0.2 does not cover
- The geometry of the class-B and class-C rules 0.2 does not fix (inferred joins but for what a framed wall layer reads of them, winders and spiral stairs, a truss layout's valleys, fittings): Part IV describes them as reference behaviour only.
- National code lists. OpenEik carries no BSAB, CoClass, AMA, SS 21054 or other national classification or code list, and has no regional profiles with such content: code lists belong to their publishers. Fields that hold such values (room area classes, rebar shape codes, steel and mesh designations) are plain strings, and a building-part catalogue is an application value (Part II, 8.3 and 9.2). Any record may name a code of any system with a neutral classification reference (Part II, 11).
- Title blocks and their images: application records (Part II, section 9). Sheets and issued drawings are records of Part XIV since 0.2 (the steward's decision of 15 October 2026).
- A protocol between stores. 0.1 says what a fetch, a push, a pull and a clone do (Part VI, 6), not how objects and refs travel from one store to another; a later version may define one (open questions, 28).
2. Conformance language
See the conventions. The conformance classes are defined in Conformance. A requirement on "an implementation" applies to every class that performs the act it names.
3. Units, axes and numbers
- Lengths and coordinates MUST be in millimetres. The project record MUST say so:
project.unitsis"mm". - Axes MUST be right-handed with Z up. The project record MUST say so:
project.upAxisis"Z". - Plan coordinates are
[x, y]pairs. A record's elevations are given by a level and an offset (section 4), never by a free Z coordinate, except where a schema names a field as an absolute elevation. - Angles in records are in degrees unless a field says otherwise. A positive turn in plan is counter-clockwise seen from above.
- Numbers MUST be finite JSON numbers. Readers MUST refuse
NaNand infinities written in any form. - Implementations SHOULD keep coordinates within ±10⁶ mm of the origin. Placement on the earth is the project's
site(latitude, longitude, true north), not a large coordinate offset. - A reader MUST refuse a model whose project record gives
unitsother than"mm"or anupAxisother than"Z", and a folder or a version whoseeik.jsongivesunitsother than"mm"(section 9.2, rule 1; Part III, 8.1), whether or not it performs the whole-model check (Part III, 12): it cannot read such a model's lengths and coordinates as this section says.
Reference: manifestSchema.project in src/core/model/model.ts (units and upAxis are literals). JSON has no literal for NaN or an infinity, and the reference reader's JSON parser refuses the tokens.
4. Levels and level semantics
A level is a named horizontal datum: {id, name, elevation} with elevation in millimetres. Every element names one level (levelId) and an offset from it. What the level plus the offset means depends on the category:
| Category | elevation(levelId) + offset is |
|---|---|
wall | the wall's base |
slab, foundation, ceiling, roof | the top face. For a roof with a roof payload: the top face along the footprint's edge lines (Part IV, 4.4) |
beam | the underside of the member at its start point |
column | the base of the column |
door, window | not the level: offset is the height of the opening's bottom (a window's sill) above its host wall's base |
shaft, stair | the base. The top is base + height, or, with top.constraint = {levelId, offset}, that level's elevation plus that offset |
- Implementations MUST apply these meanings.
- A wall's height is measured up from its base. A horizontal element's thickness is measured down from its top face.
- Every element's
levelIdMUST name a level of the model.
Reference: basicSolids and layeredBodies in src/core/geometry/geometry.ts; memberSolids in src/core/geometry/familyGeometry.ts; shaftRange (src/core/elements/shafts.ts) and stairRange (src/core/elements/stairs.ts); the opening checks in validateElement (src/core/model/model.ts), whose messages state that an opening's offset is measured from its wall's base.
5. Plan profiles
Every element has a profile: a list of one to 500 plan points. Its meaning depends on the category (Part II, section 6). The ones every reader meets:
- A wall's profile is two points: its centreline, from its start to its end.
- A beam's profile is two points: its plan axis, from its start to its end. Its section is centred across this line (Part IV, 6.2).
- A column's first point is its centre.
- A slab's, foundation's, ceiling's or roof's profile is its outline, three points or more, not closed (the last point does not repeat the first).
- A door or window takes its place from its host wall (
hostId,along), not from its profile.
5.1 The directed baseline and layer order
- A wall's profile is directed: from its first point to its second.
- A wall type's layers are listed exterior first, and the exterior is on the left of the directed baseline. The first layer's outer face lies at half the total thickness to the left of the centreline.
- A horizontal type's layers (slab, foundation, ceiling, roof) are listed top first.
Reference: strip (left normal) and layeredBodies in src/core/geometry/geometry.ts; exteriorLayerIds in src/core/elements/wallTop.ts ("layers left of the first core layer, exterior side of the directed baseline"); layerBands in src/core/elements/wallJoins.ts.
So an outer wall drawn clockwise seen from above has its exterior outside. In the example, the south wall runs from [10000, 0] to [0, 0]: its left is the south, and its first layer (render) is outside.
6. Ids
- Every record of a list has an
id: a non-empty string, unique in its list. Elements, types, views, levels, grids, sheets and linked references MUST have ids unique in their list; implementations MUST refuse duplicates there. - An id is opaque. Implementations MUST NOT derive meaning from an id's text.
- An id MUST be stable for the life of its record: an implementation MUST NOT change the id of a record it keeps. A record under another id is another record. References, diffs and merges (Part VI) and the GlobalIds of an export (Part VIII) follow the id; a record moved to another level or folder keeps it (Part III, 4), and so does every record that the change of layout of 0.2 moves (Part III, 4.4, rule 9).
- An id MUST NOT contain
/. The character is reserved for derived ids: the elements of a placed group are named<copy id>/<element id>(rule 6), so a/in a stored id would be ambiguous. The ids of originals and copies MUST NOT contain white space either (their schemas say so). - A writer that mints an id SHOULD make it unique without coordination and readable, for example
wall-k3f9qz(a kind, a hyphen and random base-32 characters). Commands that make records (Part V) never mint random ids: the caller names the id, or the command derives it deterministically. - Ids that the reference implementation derives follow fixed patterns that readers MAY rely on: a boundary edge of an outline is
<element id>:edge:<n>until the outline is edited; a placed copy's element is<copy id>/<element id>. - Paths that fold to one. Two records whose paths (Part III, 3 and 4.2) fold to one, compared name by name as Part III, 4.3 says, MUST NOT both be in a model, nor two levels whose ids give path segments (Part III, 2) that differ and fold to one, since each gives the name of its elements' folder in every discipline folder, whether or not an element stands on the level yet. A validator MUST report such a pair as an error, naming both. A writer MUST refuse a model that holds one, naming both, before it writes anything.
- The length of a name made from an id. The path segment (Part III, 2) of a level's id, a view's id, and the id of every other record kept in a file of its own MUST be at most 200 bytes of UTF-8, its escapes counted as they are written. A validator MUST report a longer one as an error, and a writer MUST refuse a model that holds one.
Reference: validateManifest (duplicate check), elementSchema.id (min(1)); madeId in src/core/model/store.ts; copyId in src/core/model/copies.ts.
7. References
A record refers to another record by its id, in a named field. No reference is made by position in a list.
| Field | Names a |
|---|---|
typeId (element) | type |
levelId (element, view), top.constraint.levelId | level |
hostId and the other element references in Part II, section 7 | element |
recesses[].typeId, edges[].typeId, roof.windows[].typeId | type |
scopeBoxId (view) | scope box |
parentViewId, dependentOn (view) | view |
datums.levelIds[], datums.gridIds[] (view) | level, grid |
- An element's
typeIdMUST name a type of the model whosecategoryequals the element's. - An element's
levelIdMUST name a level of the model. - Every other reference SHOULD resolve. A reference that the reference validator checks MUST resolve (Part II, section 7, lists which).
- Schemas mark reference fields with the annotation
x-eik-ref("type","level","element","view","grid","scopeBox").
Reference: validateElement ("Level does not exist.", "Type … is not in this project", "is a … type, not a … type"); ELEMENT_REFERENCES in src/core/model/copies.ts, the implementation's registry of element-to-element references.
8. Revisions
- Elements, types, views and the other records whose schema has a
revisionfield carry a revision: a non-negative integer. An absent revision means 0. - A command that changes a record MUST set its revision to the old revision plus one. A record a command creates gets revision 0 unless the command gives one.
- A person or agent who changes a record file by hand MUST add 1 to its revision.
- A revision is a precondition token, not a version number: commands name the revision they were based on (
expectedRevision), and a mismatch is refused (Part V). - A reader MUST treat an absent
revisionas 0. A writer MUST write the revision of every element.
Reference: run in src/core/model/store.ts (revision: old.revision + 1); elementSchema.revision (default(0)); AGENTS_TEXT in src/core/versions/eikFolder.ts.
9. Versions of the specification and of a model
9.1 This specification
This specification is versioned with semantic versions. 0.2.0-draft is a working draft: until 1.0.0, a minor version MAY break compatibility. OpenEik 0.1 is kept as it was at the tag v0.1.0 of this repository, and 0.2 is tagged v0.2.0 when its text, schemas and suite are complete; both tags are private until the public launch, which publishes them (section 9.5).
9.2 A folder's format version
- A folder states its format in
eik.json:{"format": "openeik/0.2", "units": "mm"}; a model in the single-file form states it at its top, in the same two members (Part III, 11, rule 5). - A writer MUST write
formatasopeneik/<major>.<minor>with an optional pre-release suffix (-draft), naming the version of this specification it writes. - A reader MUST read
formatbefore it reads records. If it names a major version the reader does not implement, or a 0.x minor version the reader does not implement, the reader MUST refuse the folder and say which version it found. It MAY offer to read it anyway as a best effort, but MUST NOT write to it. - A reader MUST refuse a folder or version whose
eik.jsonis not JSON or has noformat. - A writer MUST write
eik.json. A folder or version tree withouteik.jsonwas written before the format file was required (version trees before 11 October 2026 never held one). A reader MAY read it asopeneik/0.1-draftinmmonly if itsproject/project.jsonhasschemaVersion1, and then MUST warn; otherwise it MUST refuse it.
9.3 The record schema version
- In 0.1,
project/project.jsoncarriesschemaVersion, which is1. 0.2 deprecates it (section 9.6), and 1.0 removes it: the format names a model's version (section 9.2). A writer of 0.2 does not addschemaVersionto a model that gives none, and keeps one a model gives, as the migration to 0.2 keeps the project's file (section 9.8, rule 1), so that the project's hash, and every drawing and result that read it, does not change for it. - A reader MUST refuse a project whose
schemaVersionit does not implement. - Rule 2 binds a reader of a model of 0.1. A reader of a model whose format names 0.2 reads its version from the format: it does not read
schemaVersion, needs none, and refuses none for its value; it keeps one a model gives, as any member (section 10). A validator MAY say that a model of 0.2 gives one.
Reference: EIK_FORMAT and eikFolder in src/core/versions/eikFolder.ts write eik.json; manifestSchema.schemaVersion is z.literal(1), so the reference reader refuses any other value.
9.4 Which schema validates which file
Each kind of file has one JSON Schema; schemas/0.2/index.json maps path patterns to schemas, and Part III, section 3, lists them. Schemas are versioned with this specification: those of 0.2 have $ids that start with https://openeik.org/schemas/0.2/, and those of 0.1, beside them in schemas/, https://openeik.org/schemas/0.1/. A model is validated against the schemas of the version its format names.
9.5 Errata
- A version of this specification is published when its text, schemas and examples are released under a version without a pre-release suffix (
0.1.0, not0.1.0-draft). A published version is not changed afterwards. A working draft is changed in place until it is published. - A published version is corrected by errata. Errata and changes are told apart by the version's rules as they were meant. An erratum makes the version say what it already meant: it corrects a typing error, a wrong section number or link, an example or a schema that disagrees with those rules, or a rule that contradicts another where the rest of the version and its implementations show which one is meant. A corrected schema is an erratum even where a tool that follows it now accepts or refuses what it did before.
- A correction that changes the rules as they were meant is not an erratum but a change: a rule added, removed, made stricter or made looser, or a schema made to accept or refuse what those rules do not. A change goes into a later version by RFC (
CONTRIBUTING.md). Where it is not clear which of two readings a version meant, its erratum says so, and the question goes to the next version. - A patch version (
0.1.1) is a published version with its errata applied, and nothing else. A folder's format names only the major and the minor version (9.2), so a model inopeneik/0.1is in that format under every patch version of 0.1, and a claim of conformance to a version (Conformance) is a claim to its text as its errata correct it. The schemas'$ids name only the minor version (9.4):https://openeik.org/schemas/0.1/…serves the schemas of 0.1's latest patch version, every erratum applied, and the repository's tag of each patch version (v0.1.1) keeps its text and schemas as they were published. - A reader of a published version MUST read a model whose format names a pre-release of that version (
openeik/0.1-draft, for 0.1) as a model of that version, and SHOULD warn that the draft may differ from it. A writer of a published version writes its format without a pre-release suffix (9.2, rule 2). - The errata of each published minor version are listed in one place, each with a number and a date.
GOVERNANCE.mdsays how they are reported, decided and announced.
9.6 Deprecation, and reading an earlier version
- A version may deprecate a member, a value, a kind of record or file, or a rule. It stays part of that version and of its schemas, and a reader reads it as that version says. A writer SHOULD NOT write it; the version says what to write instead, and may forbid writing it outright, as Part II, 6.4 forbids the legacy members of a wall's top.
- Only a version that may break compatibility removes what an earlier version deprecated: before 1.0 a minor version (9.1), from 1.0 a major version. From 1.0 a minor version adds and deprecates, and neither removes nor changes what an earlier minor version of its major version defined, so a reader of 1.y reads a model of any 1.x before it.
- A reader SHOULD also read a model of the version that its own version replaced (before 1.0 the previous minor version, from 1.0 the previous major version; for 1.0 itself, the last 0.x minor version), by the rules of that version. A reader that does not refuses such a model, as 9.2, rule 3 says, naming the version it found.
- A version that breaks compatibility says in its changelog how a model of the version before it is migrated. A writer that migrates a model writes the format of its own version (9.2, rule 2), and SHOULD tell the person that it did. Section 9.8 says how a model of 0.1 is migrated to 0.2.
9.7 A file's own format
Some files name a format of their own, apart from the folder's (9.2): a member format that names the kind of file and its version, such as openeik-result/0.1 and openeik-proposal/0.1 (Part VI, 8), openeik-owners/0.1 (Part XVI, 2), openeik-envelope/0.1 (Part XVII, 2), the openeik-drawing-* formats of Part XIV, openeik-model-review/0.1 and openeik-host-keys/0.1 (Part XVIII); or, in a signed statement, a predicate type, such as https://openeik.org/attestation/issue/v0.1 (Parts XIV and XVIII).
- It versions on its own. Such an id is the version of that kind of file, not of this specification nor of a folder. A version of this specification keeps a file's format id while it keeps that file's form, so a writer of 0.2 writes
openeik-owners/0.1for as long as 0.2 defines the owners file as 0.1 does. A file's form that changes in a way its readers must know gets a new id, its minor or its major number moved, and the part that defines the file says which ids a reader reads and which a writer writes. - Read by its own id. A reader reads such a file by its own format id, whatever the format of the folder or version that holds it, and refuses or ignores a file of an id it does not implement as the part that defines the file says.
- Its schema follows this specification. A file's schema is versioned with this specification (9.4): at 0.2 its
$idmoves from underschemas/0.1/to underschemas/0.2/of this specification's site, while its format id stays what it was.
9.8 Migrating a model of 0.1 to 0.2
0.2 breaks compatibility with 0.1 in its folders, in what it deprecates and in its sheets. A writer migrates a model of 0.1 to 0.2 so; @openeik/tools does (migrateFiles, migrateFolder and openeik migrate), and the changelog says the same (9.6, rule 4).
- One write. A writer migrates a folder of 0.1 in one write, and a building's history in one version: its
eik.jsonnamesopeneik/0.2(section 9.2), and its project's file stays as it is, itsschemaVersionwith it, which a reader of 0.2 does not read (section 9.3); each record whose folder changes moves to the folder of its owner (Part III, 4.4, rule 8);project/owners.json, where the folder has one, is rewritten for the new folders (Part XVI, 3.1); and rules 2 to 5 are applied. Every other record keeps its object, and so its hash, its id and the GlobalId an export gives it, and every other file its bytes. The version needs the owners of each path it touches, by the owners file as it stood before (Part XVI, 4, rule 1), and SHOULD be made by a party that owns**. Versions already stored are never rewritten (Part VI, 3, rule 2). The writer tells the person what it moved, what it rewrote and what stays as it was. A model in the single-file form is migrated by the same rules, its format and units at its top (Part III, 11, rule 5); it has no folders to move. - An emergency luminaire of the
firecategory (Part XIII, 2.3, rule 7). A writer SHOULD rewrite afiretype whosefire.kindisemergencyLightas anelectricaltype of the same id: itselectricalthe kindemergencyLight, and thefirepayload'smount,depth, andoffsetas itsdefaultOffset; itswidthandheightthe payload's; every other member of the type kept butfire. Each element of the type becomes anelectricalelement of the type'swidthandheight, every other member kept: aprofileof two points at least 1 mm apart stays as it is, and one of one point gets a second 1000 mm from it along plan +y, so that it faces plan +y, as a terminal of one point does (Part II, 6.9). It is on no circuit until one is given it (Part XIII, 3). The payload'scode,accessibleandagent, which an electrical type does not have, are left out, and the writer says so. A type with an element it cannot rewrite so, of another category or of another profile, stays as it is, with its elements, and the writer says so. - A toilet or a basin modelled as a component (Part II, 5). A writer SHOULD rewrite a
componenttype whosecomponent.familyistoiletorbasinas aterminaltype of the same id: itsterminalthe kindwcfor a toilet andwashbasinfor a basin, the mountfloor, itswidthandheightthe type's and itsdepththe type'scomponent.depth; every other member kept butcomponent. Each element of the type becomes aterminalof the type'swidthandheight, withouthostId,along,horizontalOffset,verticalOffset,flipped,handFlipped,componentInvertedandstructural, every other member kept: with a and b itsprofile's first two points, x the direction from a to b and y the component's local +y, x turned a quarter to the left, or to the right where it isflipped, towards its cistern or its tap, itsprofileis[a, a − 1000·y], each coordinate to 0.1 mm: its back stays where the component's was, and it faces where the component's bowl pointed. A type whose sizes no terminal type takes, or with an element of another category or whose a and b are one point, stays as it is, with its elements, and the writer says so. - A railing on an outline (Part II, 12.2.1). A writer SHOULD leave out the
profileof a railing on the outline of a slab or a shaft, on a wall's top or along a slab edge element, which a reader of 0.2 ignores. - Sheets (Part XIV, 2). A sheet of 0.1 that gives every member Part XIV, 2.1 requires of a sheet,
number,name,revision,width,heightandviewports, is a sheet of Part XIV in 0.2, written as it is. Any other stays an application record, written as it is (Part XIV, 2.1, rule 1), and the writer names it and what it does not give (sheet-application-record).
10. Unknown members are kept
A record may hold members that a reader does not know: members of a later version, of a profile the reader does not implement, or of another application (its extension, section 11). An element or a type may also be of a category the reader does not know (rule 5).
- A reader MUST keep every member it does not know, at any depth, and MUST write it back unchanged when it writes the record.
project/order.jsonis no record but an index of the record lists, and Part III, 9, rule 5 says what a reader keeps of it. Nor iseik.jsona record: Part III, 8.1 has a writer keep the members of it that it does not know. - A validator MAY warn about a member it does not know. It MUST NOT refuse a record for one.
- The schemas say which members they know with the annotation
"x-eik-closed": trueon an object; they do not refuse others. The annotation means whatunevaluatedProperties: falsewould mean in its place (JSON Schema 2020-12): a member of the object is known when a subschema that applies to the object evaluates it, byproperties,patternPropertiesoradditionalProperties, and unknown when none does. So whereallOfor$refcombine subschemas, a member that any of them names is known, and an alternative ofanyOforoneOfadds its members only when the object meets it. It does not meanadditionalProperties: false, which sees only the members its own subschema names. A validator that wants to warn can read the annotation so, astools/check.mjsand the packagepackages/openeikdo. - A member a category does not allow (Part II, 6.3) is a known member in the wrong place, not an unknown one: a validator MUST report it as an error, and a record that holds one fails the whole-model check (Part III, 12, rule 5).
- Unknown categories are kept. An element or a type of a category that an implementation does not know, such as one a later version adds, is kept, so that a later version can add categories without breaking the tools of this one:
- a reader MUST keep it, with all its members, and MUST write it back unchanged: at the path it was read from, or, where it has none (the single-file form), under
architecture(Part III, 4.1); - a validator MUST NOT refuse it, and SHOULD warn about it; it validates it against the common element or type schema only (
elements/element.schema.json,types/type.schema.json); - a compiler derives no body for it unless it carries declared geometry it can place, and says which it did (Part IV, 1);
- a command processor MUST NOT create one, and MUST keep those a model holds (Part V, 1).
- a reader MUST keep it, with all its members, and MUST write it back unchanged: at the path it was read from, or, where it has none (the single-file form), under
11. Extensions
An application may add data of its own to a model: members in records, and files in the folder. Every reader keeps both already (section 10; Part III, 7). This section keeps them apart: what an application adds carries a prefix registered to it, so that two applications never give one name two meanings, and no later version of this specification takes a name that an application uses. What an application adds in this way is its extension.
11.1 Prefixes and the registry
- A prefix is 2 to 32 lowercase ASCII letters, digits and hyphens. It starts with a letter, ends with a letter or a digit, and has no two hyphens side by side (
^[a-z][a-z0-9]*(-[a-z0-9]+)*$). Since it also names a folder (11.3), it is not a device name of a common operating system:con,prn,aux,nul,com0tocom9,lpt0tolpt9. - The extension registry,
registry/extensions.jsonin this specification's repository, lists the registered prefixes, each with its registrant, a contact and a short description, and the reserved names:eikandopeneik, every prefix that starts witheik-oropeneik-, the names this specification gives to folders at the top of a model's folder (Part III, 1), andassets, which it plans to give one.registry/README.mdsays how a prefix is registered;schemas/registry/extensions.schema.jsonchecks the registry's form. - An application MUST NOT use a reserved name as its prefix, nor a prefix registered to another registrant. It SHOULD register its prefix before it writes a model that other people or applications read.
- A prefix stands for one extension for good: the registry never gives it to another registrant for another use, even after its registrant withdraws it, so that the models that carry it keep one meaning.
11.2 Extension members
- An extension member is a member named
<prefix>:<name>: a prefix that is not reserved, a colon, and a name of the application's own that is not empty, for exampleacme:fireRating. It is a member of an object whose members a schema of this specification names, one the schema marks"x-eik-closed": true(10.3): a record or an object within one, as a file holds it or a command carries it (Part V, 1, rule 3), or an object of a result manifest or a proposal file (Part VI, 8.2 and 8.7), such as a manifest'smade. The keys of an object that is a map, keyed by ids or names (a railing'sinsets, by edge id; a view'svisibility.elementOpacity, by element id), are data, not members: a key of that form there is no extension member. - An application that adds a member to an object whose members a schema names MUST make it an extension member under its own prefix. A writer MUST NOT write into such an object a member that this version does not define, unless it is an extension member or a member the writer keeps from a record it read (10.1).
- A writer MUST NOT add an extension member whose value is
null: in a patch,nulltakes a member away (Part V, 4.2), so no update could write it as a value. Any other JSON value is the application's to choose. An extension member that a writer read is not one it adds: it writes it back unchanged,nullor not (10, rule 1). Part V, 1, rule 3 says what a command processor does with a command that would add one. - An extension member is part of its record: a reader keeps it (10.1), it counts in the record's hash (Part III, 6), a change to it changes the record's revision as any change does (section 8), diffs and merges treat it as any member (Part VI, 4 and 5), a precondition that compares the record compares it too (Part V, 3, rule 3), and commands write it as they write any member: a patch replaces it whole, and
nullremoves it (Part V, 4). - A reader, a validator, a compiler and a command processor MUST treat a model with extension members as they treat the same model without them, but for keeping them, as rule 4 says, and for what a validator reports about them (rule 9). Keeping them includes comparing them: a precondition that names a record's state compares the whole record, extension members included (Part V, 3, rule 3), so a command based on a record as it was read without an extension member that the record holds now is refused, as a command based on any other earlier state of the record is. An extension member names nothing: a command processor does not follow one to find what depends on a record (Part V, 5), and a compiler derives nothing from one. So a reader needs no extension to read a model, and a model does not list the extensions it carries.
- An extension adds members and files only. Categories, record lists and the values of a list that a schema fixes (an enumeration) come only with versions of this specification (section 10, rule 5).
- Part IV, 1, rule 2 holds for extension members as for every member: derived results are not stored in records. An application keeps what it computes in its folder, with a result manifest (Part VI, 8).
- An application MUST NOT add a member to
eik.json, an extension member or any other, since only a later version of this specification extends it (Part III, 8.1), nor a member toproject/order.json, an index of the record lists and not a record, which names the lists only and has no counterpart in the single-file form (Part III, 9, rule 5, and 11), nor an extension member to the top of a result manifest or a proposal file, where its own data goes in the members that Part VI, 8 leaves to it (engine,summary,evidence). In the other objects of those files whose members a schema names (a manifest'sappandmade, a proposal'sbyand each entry of itsbase.records) it adds extension members as in a record (rules 1 and 2), and the records and patches that a proposal's commands carry are records, and carry extension members as any record does. - A validator MUST NOT refuse a record for an extension member (10.2). It SHOULD tell extension members apart from the other members it does not know, which are often misspellings, and MAY warn about an extension member whose prefix is not registered, where it has the registry.
11.3 An application's files
- An application keeps its own files (Part III, 7.7; Part VI, 8.1) in a folder at the top of the model's folder, which SHOULD be named by its prefix:
<prefix>/, with a model folder for each model of its own (acme/frame-a/). - An application MUST NOT write into a folder at the top of the model's folder whose name is reserved or is a prefix registered to another registrant, other than the records it writes at their paths (Part III, 3 and 4), and SHOULD NOT write kept files anywhere but in its own folder. In this rule a folder's name is compared with the reserved names and the registered prefixes without regard to ASCII case, as the file systems of common operating systems compare names:
ACME/is the folder of the prefixacme, andTypes/a folder of the reserved nametypes, which an application does not write into. No other comparison of names follows from this rule.
11.4 From an extension into the core (informative)
This section is informative: it says how this specification grows, not what an implementation does.
- An extension becomes part of this specification only by an RFC (
CONTRIBUTING.md), in a later version, which names its members without a prefix and may change their shape. Its registrant is asked to take part. - That version says which extension members its own members replace, and how its readers read a record that still carries them: as its own member, as Part II, 6.4 reads legacy members, or not at all. An extension member keeps the meaning its registrant gave it; the new member has the meaning the version gives it.
- The prefix stays registered to its registrant, and the registry notes what was taken into the core, in which version and under which name.
11.5 The reference implementation's own members
Part of 0.2. OpenEik 0.1 had it as the proposal adopted (Part II, 14): in a model of 0.1, read by 0.1's rules (section 9.6, rule 3), rule 2 does not apply.
By the steward's decision of 30 October 2026 (open questions, 115). The reference implementation wrote, in models of 0.1, members of its own without a prefix, which section 11.2, rule 2 does not allow. Those that another tool needs to read, draw, check or print the building join 0.2 (Part II, 4, 7 and 14; Part IV, 4.6). Those that only its own interface reads are its extension members, under its registered prefix stomra (section 11.1):
| In a model of 0.1 | From 0.2 | What it is |
|---|---|---|
an element's person | stomra:person | a person's free-height limit: the height its interface holds the free height over the person to, 2000 mm where it gives none |
a type's person | stomra:person | the figure its interface draws a person by, and the person's height, which the type's own height gives too |
a view's display.colours | stomra:colours, in display | its colour mode: in colour, in grey, or in black lines on white |
a view's calloutShape | stomra:calloutShape | the shape of its detail callout's mark and crop: a circle, a rectangle or a rounded rectangle |
a view's calloutLabelOffset | stomra:calloutLabelOffset | where that mark's label stands, from the marked area's middle, moved by hand |
a view's calloutCornerRadius | stomra:calloutCornerRadius | the corner radius of the rounded mark |
a view's breakLines | stomra:breakLines | whether that callout's crop is drawn with its dash-dot break lines |
a detail family's dimensions | stomra:dimensions | the dimensions its family editor shows the family's bindings by, and makes them with; the bindings are the geometry |
- The rename. From 0.2 the reference implementation writes each of them as the extension member the table names, in the object that held it, its value unchanged; and a person, which has no category of its own in 0.2, as a
genericelement whose type stands for a person (Part II, 6.6, rule 9; the 0.2 plan's decision D9). Section 11.2 holds for them as for every extension member: every reader keeps them, and no other tool needs them. - An old one. A reader of 0.2 that reads one of them without its prefix keeps it as a member it does not know (section 10, rule 1): it does not read it as the extension member, and does not rename it. A validator SHOULD warn of it, naming the extension member it stands for (
member-unprefixed). - Migrating. The reference implementation renames its own in its migration of a model to 0.2 (section 9.6, rule 4), which may be the version that changes its layout (Part III, 4.4, rule 8). It reads them, in a model of 0.1, as its extension members.