# Part I. Foundations

## 1. Scope

OpenEik specifies:

1. **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).
2. **A code base**: how a model is stored as a folder of files, one record per file, in a canonical text form (Part III).
3. **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).
4. **Commands**: the typed, validated, all-or-nothing writes that change a model (Part V).
5. **History**: content-addressed versions, diffs and three-way merges by record and field (Part VI).
6. **An agent interface**: how an AI agent reads, searches and changes a model (Part VII).
7. **Bridges**: IFC printed from a version of a model (Part VIII).
8. **Electrical installations**: devices and boards, circuits, control, cable carriers and modelled cables (Part XIII).
9. **Drawings**: sheets, and drawings issued, reviewed and signed (Part XIV).
10. **Assets**: binary files kept by their digests (Part XV).
11. **Ownership**: the parties of a project, the paths each owns and the approvals a change needs (Part XVI).
12. **Commands with an envelope**: who sends a batch, its base and mode, and refusals with codes (Part XVII).
13. **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](https://openeik.org/spec/0.4/drawings) 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](https://openeik.org/spec/0.4/#conventions-of-this-text). The conformance classes are defined in [Conformance](https://openeik.org/spec/0.4/conformance). A requirement on "an implementation" applies to every class that performs the act it names.

## 3. Units, axes and numbers

1. Lengths and coordinates MUST be in millimetres. The project record MUST say so: `project.units` is `"mm"`.
2. Axes MUST be right-handed with Z up. The project record MUST say so: `project.upAxis` is `"Z"`.
3. 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.
4. Angles in records are in degrees unless a field says otherwise. A positive turn in plan is counter-clockwise seen from above.
5. Numbers MUST be finite JSON numbers. Readers MUST refuse `NaN` and infinities written in any form.
6. 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.
7. A reader MUST refuse a model whose project record gives `units` other than `"mm"` or an `upAxis` other than `"Z"`, and a folder or a version whose `eik.json` gives `units` other 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.

*Note (informative).* The ±10⁶ mm extent is advice in 0.1: the reference implementation does not refuse larger coordinates. See the open questions (F10). Rule 7 states, as a reading of the steward's decision of 15 October 2026 on the whole-model check (open questions, 29), that units and an up axis other than this section's are among what no reader can read (Part III, 12). A project record or an `eik.json` that gives no `units` fails the check, and is no model in other units.

## 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 |

1. Implementations MUST apply these meanings.
2. A wall's height is measured up from its base. A horizontal element's thickness is measured down from its top face.
3. Every element's `levelId` MUST name a level of the model.

*Note (informative).* In 0.2, for a `member`, a member between two points (Part II, 6.2.1), the level plus the offset is the start of its reference line, and its `end` gives its end's level and offset; for a slab that carries a `plane` (Part II, 6.3.1), it is its top face's height along the edge the plane names, or at its profile's first point.

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.

*Note (informative).* Until Stomra pull request #778 (work 476, live since 11 October 2026) the `AGENTS.md` that the reference writer puts in every folder said an opening's offset is "above the level", the same number only when the host wall's own offset is 0. It now says "above its wall's base", as the code measures, and the examples carry that text since 14 October 2026. See the open questions.

## 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

1. A wall's profile is directed: from its first point to its second.
2. 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.
3. 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

1. 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.
2. An id is opaque. Implementations MUST NOT derive meaning from an id's text.
3. 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).
4. **A `/` in an id.** The character joins the parts of a derived id: the elements of a placed group are named `<copy id>/<base id>` (rule 6; Part II, 10.1). A stored id MAY contain it as section 6.1 says, which keeps a stored id from being a derived one; the ids of originals and copies MUST NOT contain it, nor white space (their schemas say so). In 0.2 no id contains it.
5. 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.
6. 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>/<base id>`, and the elements of a group's first instance, where it is not the one the group keeps, are their base ids (Part II, 10.1, rule 3).
7. **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.
8. **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`.

*Note (informative).* Rules 3 and 4 were made normative on 11 October 2026, rule 4 then forbidding a `/` in every id; 0.3 lets a stored id hold one (section 6.1; open questions, 126). The reference implementation accepts `/` in every id but those of originals and copies, and escapes it as `%2F` in paths (Part III, 2). The schemas of 0.2 state 0.2's rule on every record's id (`"pattern": "^[^/]+$"`), and those of 0.3 on the ids of originals and copies alone (`^[^/\s]+$`). The reference implementation also accepts an empty type id and does not check uniqueness in every list (materials, scope boxes and the other library lists are keyed by id in memory, so a duplicate replaces the earlier record). See the open questions.

*Note (informative).* Rules 7 and 8 were decided by the steward on 15 October 2026 (open questions, 23 and 26). Rule 1 compares ids as they are written, and rule 7 the names a file system is given: two materials with the ids `Steel` and `steel` are two ids, and one file where case is ignored, so they are not both in a model; the ids `con` and `Con` fold to one, but their segments, `%63on` and `%43on`, do not, and every file system holds both. Under rule 8 a record's file name, its segment and `.json`, is at most 205 bytes: within the 255 bytes of UTF-8 that Linux takes for a name (Windows and macOS take 255 characters, which such a name always fits), with room for the names that tools make beside a file. The schemas cannot count bytes of a segment, so a validator checks rule 8 in code. The minting pattern of rule 5 stays a recommendation: whether writers MUST mint ids by a pattern was deferred to 0.2, by the steward's decision of the same day (open questions, 6). The reference implementation meets neither rule yet: it compares no paths folded, and bounds no id's path segment (open questions, section 2).

### 6.1 A slash in a stored id

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

These rules are rule 4's in 0.3, and Part II, 10.1 says what a group keeps and how its instances name their elements. A stored id is then what rule 4 was there to keep it: never an id a copy derives, and an id that can be told from a derived one by its form; and a group written out at another of its instances keeps each element's id, as rule 3 asks. In a model of 0.2 that does not ask for the proposal, rule 4 stands.

1. **A `/` in a stored id.** A stored id MAY contain `/`: a record's, a level's and a view's, but for an original's and a copy's (rule 3). A path writes it as Part III, 2 escapes it, `%2F`, so that it names one file; and where a reference joins a list's name and an id, `<list>/<id>` (Part IV, 1.1.2; Part XIV's `data-eik-record`), it is read at its first `/`, as no list's name holds one.
2. **Not a derived id.** A stored id MUST NOT begin with the id of a copy of the model and a `/`, unless that copy is its original's frame: the instance whose elements are the stored ones, which derives none (Part II, 10.1, rule 1). So no stored id is an id a copy derives, which begins so (Part II, 10.1, rule 3), and an id that begins with a copy's id and a `/` where that copy is not its original's frame is a derived one. A validator MUST report a stored id that breaks this rule (`id-slash`).
3. **What holds none.** The ids of originals and copies, an original's `frame`, and an element's `original`, `born` and `copy` hold no `/` and no white space, as their schemas say, so that `<copy id>/` is read at its first `/`. A validator MUST report an original's or a copy's id that holds a `/` (`id-slash`).
4. **The ids derived.** Rule 6's pattern of a placed copy's element, `<copy id>/<element id>`, is Part II, 10.1, rule 3's: an element's id in an instance it was not drawn in is the instance's id, a `/` and its base id.

Reference: `copyId` and `validateCopies` in `src/core/model/copies.ts`; `frameOf`, `baseId` and `idIn` in `src/core/model/groups.ts`. In `@openeik/tools`: `slashes` in `packages/openeik/src/check.js`, for a model of 0.3, and for one of 0.2 asked for with `proposals: ["groupIds"]`.

*Note (informative).* The reference implementation stores such ids for a group written out at another of its instances (`original.frame`): that instance's elements are then the stored ones, each under the id it had as a derived element, such as `P2/slab-01`, so that whatever named it still does, and the copy `P2` stays and derives none of them. 0.2's rule 4 refuses every such model. The editors decided on 31 October 2026, the steward having left the choice to them, that a stored id may hold a `/` in 0.3 where it is no derived id (open questions, 126); this section was written as a proposal for 0.3, which the cut of 0.3 made a rule until the cut of 0.3 makes it a rule. As elsewhere, an id of a record is a token with no separator in it, and a `/` joins two such parts in a reference: a framed group's `<frame>/<base id>` is one, neither part holding a `/`.

## 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 |

1. An element's `typeId` MUST name a type of the model whose `category` equals the element's.
2. An element's `levelId` MUST name a level of the model.
3. Every other reference SHOULD resolve. A reference that the reference validator checks MUST resolve (Part II, section 7, lists which).
4. 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

1. Elements, types, views and the other records whose schema has a `revision` field carry a revision: a non-negative integer. An absent revision means 0.
2. 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.
3. A person or agent who changes a record file by hand MUST add 1 to its revision.
4. 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).
5. A reader MUST treat an absent `revision` as 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: until 1.0.0, a minor version MAY break compatibility. This text is OpenEik 0.4, tagged `v0.4.0` of this repository on 1 November 2026. OpenEik 0.1 is kept as it was at the tag `v0.1.0`, 0.2 at the tag `v0.2.0` and 0.3 at the tag `v0.3.0`, each with the patch versions that apply its errata (`v0.1.1`; `v0.2.1` to `v0.2.3`; section 9.5); every tag is private until the public launch, which publishes them. 0.3 keeps every rule of 0.2 and adds its own, and 0.4 every rule of 0.3, each section that a version adds marked so ("*Part of 0.3.*", "*Part of 0.4.*"): where this text says that a rule is of 0.2 or of 0.3, or names what a reader or a writer of 0.2 or of 0.3 does, it holds for every later version too, unless it says otherwise.

### 9.2 A folder's format version

1. A folder states its format in `eik.json`: `{"format": "openeik/0.4", "units": "mm"}`; a model in the single-file form states it at its top, in the same two members (Part III, 11, rule 5).
2. A writer MUST write `format` as `openeik/<major>.<minor>` with an optional pre-release suffix (`-draft`), naming the version of this specification it writes.
3. A reader MUST read `format` before 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.
4. A reader MUST refuse a folder or version whose `eik.json` is not JSON or has no `format`.
5. A writer MUST write `eik.json`. A folder or version tree **without** `eik.json` was written before the format file was required (version trees before 11 October 2026 never held one). A reader MAY read it as `openeik/0.1-draft` in `mm` only if its `project/project.json` has `schemaVersion` 1, and then MUST warn; otherwise it MUST refuse it.

### 9.3 The record schema version

1. In 0.1, `project/project.json` carries `schemaVersion`, which is `1`. 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 add `schemaVersion` to 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.
2. A reader MUST refuse a project whose `schemaVersion` it does not implement.
3. 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.

*Note (informative).* Until Stomra pull request #778 (work 476, live since 11 October 2026) the reference reader did not read `eik.json` at all. Since #778 it refuses a folder or version naming other units or an unsupported version, reads one without `eik.json` with a warning (rule 5), and every new version tree holds `eik.json` as a root leaf of its canonical JSON. `@openeik/tools` refuses a folder without `eik.json` unless `assumeFormat` is set, which rule 5 allows.

*Note (informative).* A project record without `schemaVersion` names no version for 9.3, rule 2 to refuse: it fails the whole-model check (rule 1 and the project's schema), and a reader that does not perform the check reads it as the version its `eik.json` names, as section 3's note settles a project record that gives no `units`. A folder without `eik.json` needs `schemaVersion` 1 to be read at all (9.2, rule 5).

### 9.4 Which schema validates which file

Each kind of file has one JSON Schema; [`schemas/0.4/index.json`](https://openeik.org/schemas/0.4/index.json) maps path patterns to schemas, and Part III, section 3, lists them. Schemas are versioned with this specification: those of 0.4 have `$id`s that start with `https://openeik.org/schemas/0.4/`, those of 0.3, in [`schemas/0.3/`](https://openeik.org/schemas/0.3/index.json), `https://openeik.org/schemas/0.3/`, those of 0.2, in [`schemas/0.2/`](https://openeik.org/schemas/0.2/index.json), `https://openeik.org/schemas/0.2/`, and those of 0.1, at the top of [`schemas/`](https://openeik.org/schemas/0.1/index.json), `https://openeik.org/schemas/0.1/`. A model is validated against the schemas of the version its format names.

### 9.5 Errata

1. 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`, not `0.1.0-draft`). A published version is not changed afterwards. A working draft is changed in place until it is published.
2. 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.
3. 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`](https://gitlab.com/openeik/spec/-/blob/main/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.
4. 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 in `openeik/0.1` is 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' `$id`s 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.
5. 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).
6. The errata of each published minor version are listed in one place, each with a number and a date. [`GOVERNANCE.md`](https://gitlab.com/openeik/spec/-/blob/main/GOVERNANCE.md#errata) says how they are reported, decided and announced.

### 9.6 Deprecation, and reading an earlier version

1. 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.
2. 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.
3. 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.
4. 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, and section 9.9 how a model of 0.2 is migrated to 0.3.

*Note (informative).* How long implementations go on reading a version after the version that replaces it is published is a policy of the project, not a rule of this text: [`GOVERNANCE.md`](https://gitlab.com/openeik/spec/-/blob/main/GOVERNANCE.md#deprecation-and-how-long-versions-are-read) asks for at least 12 months when the version that replaces it is a 0.x version, and at least 24 months when it is 1.0 or later, so the last 0.x version, which 1.0 replaces, has 24. Until that period ends, it asks implementations to read the version whichever later version they implement, so the policy can ask more than the one version rule 3 names. Sections 9.5 and 9.6 were decided by the steward on 14 October 2026, with the process in `GOVERNANCE.md` (open questions, 21). No version of OpenEik is published yet: 0.1 is kept as it was at the tag `v0.1.0`, and 0.2.0-draft is a working draft.

### 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).

1. **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.1` for 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.
2. **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.
3. **Its schema follows this specification.** A file's schema is versioned with this specification (9.4): at 0.2 its `$id` moves from under `schemas/0.1/` to under `schemas/0.2/` of this specification's site, while its format id stays what it was.

*Note (informative).* Rule 1 was decided by the steward on 30 October 2026 (open questions, section 4, F): a file of these formats is read on its own, often by a tool that never reads the folder it came from, a host's keys by a verifier or a signed issue by a recipient, so its format says the version of its own form. Moving every such id with each version of this specification would make a 0.1 reader refuse an owners file or a signed statement that had not changed.

### 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).

1. **One write.** A writer migrates a folder of 0.1 in one write, and a building's history in one version: its `eik.json` names `openeik/0.2` (section 9.2), and its project's file stays as it is, its `schemaVersion` with 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.
2. **An emergency luminaire of the `fire` category** (Part XIII, 2.3, rule 7). A writer SHOULD rewrite a `fire` type whose `fire.kind` is `emergencyLight` as an `electrical` type of the same id: its `electrical` the kind `emergencyLight`, and the `fire` payload's `mount`, `depth`, and `offset` as its `defaultOffset`; its `width` and `height` the payload's; every other member of the type kept but `fire`. Each element of the type becomes an `electrical` element of the type's `width` and `height`, every other member kept: a `profile` of 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's `code`, `accessible` and `agent`, 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.
3. **A toilet or a basin modelled as a component** (Part II, 5). A writer SHOULD rewrite a `component` type whose `component.family` is `toilet` or `basin` as a `terminal` type of the same id: its `terminal` the kind `wc` for a toilet and `washbasin` for a basin, the mount `floor`, its `width` and `height` the type's and its `depth` the type's `component.depth`; every other member kept but `component`. Each element of the type becomes a `terminal` of the type's `width` and `height`, without `hostId`, `along`, `horizontalOffset`, `verticalOffset`, `flipped`, `handFlipped`, `componentInverted` and `structural`, every other member kept: with a and b its `profile`'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 is `flipped`, towards its cistern or its tap, its `profile` is `[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.
4. **A railing on an outline** (Part II, 12.2.1). A writer SHOULD leave out the `profile` of 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.
5. **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`, `height` and `viewports`, 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`).

*Note (informative).* Rules 2 to 4 are SHOULDs, since a reader of 0.2 still reads what they rewrite (section 9.6, rule 1). Rule 3 turns a component as the reference implementation turns one on load (`migrateComponentFixtures`, its work 268), but that the reference replaces the type with a WC and a basin of its own library, of their own sizes, where these rules make the terminal of the model's type, of its sizes: the terminal's back is then where the component's was. Where the model's component type is the reference's, the two give one place. `conformance/migration/` holds a folder of 0.1 and the folder of 0.2 these rules make of it. A model of 0.1 that used a proposal of 0.1 for 0.2, a legend of the devices or a circuit, say, is the model of 0.2 it becomes once migrated, since 0.2 makes every such proposal a rule: `@openeik/tools` reads a folder it migrates by all of them, without being told which it used, and checks the folder of 0.2 it makes rather than the folder of 0.1, since a proposal of 0.1 may be stricter than the rule 0.2 made of it (rule 5's sheets).

### 9.9 Migrating a model of 0.2 to 0.3

*Part of 0.3.* 0.3 deprecates nothing and changes the meaning of no member: it adds members and rules (open questions, 116, 121 to 123, 125 and 126; the 0.3 cut's decisions), and widens rules of 0.2, so that a model of 0.2 is one of 0.3 once it says so. A writer migrates a model of 0.2 to 0.3 so; `@openeik/tools` does (`migrateFiles` and `migrateFolder` with `to: "openeik/0.3"`, and `openeik migrate --to 0.3`).

1. **Its format alone.** A writer migrates a folder of 0.2 in one write, and a building's history in one version: its `eik.json` names `openeik/0.3` (section 9.2), and nothing else changes. A model in the single-file form names `openeik/0.3` at its top. Every record keeps its object, and so its hash, its id and any GlobalId it holds.
2. **A model of 0.1** is migrated to 0.2 as section 9.8 says, and so to 0.3, in the same write.
3. **What 0.3 now defines.** A member a writer of 0.2 gave that 0.3 now defines, as an element's `littera`, `tenderPart` and `action` or a material's `category` (Part II, 4.2 and 11.1), is read as 0.3 defines it: 0.3 took it as it was written. A member an application wrote of its own, under no prefix, stays as it is (section 10): the reference implementation's `costCodes`, and the `proposed` and `note` of its codes, among them. The application that wrote it, which alone knows what it means, writes it in 0.3's form, a classification reference of Part II, 11.1, under its prefix where 0.3 has no member for it (open questions, 116).
4. **A model that does not check.** A model of 0.2 that 0.2 refuses but 0.3 does not, as one with a rebar element in a wall or a stored id with a `/`, is judged by the model of 0.3 it becomes: a writer checks the migrated model by the rules of 0.3, and migrates it only where it passes.

*Note (informative).* The migrating version holds the migration alone (Part III, 4.4, rule 8, read with this section): an edit the building needs to pass the checks of 0.3 goes into a version before it, and the change the person was making into one after it. A writer that writes a line of 0.3 to a host that does not read 0.3 yet waits until it does.

### 9.10 Migrating a model of 0.3 to 0.4

*Part of 0.4.* 0.4 deprecates nothing and changes the meaning of no member: it adds a material's `masonry` (Part II, 4.3), a provision for a terminal (Part II, 6.10.1), what a derived member bears on (Part IV, 7.4.13.1 and 7.4.15.1), what a store reads (Part VI, 6.7) and a reader of IFC that recognises walls, beams and columns, slabs and foundations (Part VIII, 2.6.1 to 2.6.3) (open questions, 127 to 130; the 0.4 cut's decisions), and widens rules of 0.3, so that a model of 0.3 is one of 0.4 once it says so. A writer migrates a model of 0.3 to 0.4 so; `@openeik/tools` does (`migrateFiles` and `migrateFolder` with `to: "openeik/0.4"`, and `openeik migrate --to 0.4`).

1. **Its format alone.** A writer migrates a folder of 0.3 in one write, and a building's history in one version: its `eik.json` names `openeik/0.4` (section 9.2), and nothing else changes. A model in the single-file form names `openeik/0.4` at its top. Every record keeps its object, and so its hash, its id and any GlobalId it holds.
2. **A model of 0.1 or 0.2** is migrated to 0.3 as sections 9.8 and 9.9 say, and so to 0.4, in the same write.
3. **What 0.4 now defines.** A member a writer of 0.3 gave that 0.4 now defines, a material's `masonry` (Part II, 4.3), is read as 0.4 defines it: 0.4 took it as it was written. A member an application wrote of its own, under no prefix, stays as it is (section 10), for the application that wrote it to write in 0.4's form. Nothing that 0.4 derives, a member's bearings among it, is a record, so a migration writes none of it (Part II, 10).
4. **A model that does not check.** A model of 0.3 that 0.3 refuses but 0.4 does not, as one with a provision for a terminal, is judged by the model of 0.4 it becomes: a writer checks the migrated model by the rules of 0.4, and migrates it only where it passes.

*Note (informative).* As with 0.3, the migrating version holds the migration alone (Part III, 4.4, rule 8): an edit the building needs to pass the checks of 0.4 goes into a version before it, and the change the person was making into one after it. A writer that writes a line of 0.4 to a host that does not read 0.4 yet waits until it does; a store that states what it reads says so (Part VI, 6.7).

## 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).

1. 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.json` is no record but an index of the record lists, and Part III, 9, rule 5 says what a reader keeps of it. Nor is `eik.json` a record: Part III, 8.1 has a writer keep the members of it that it does not know.
2. A validator MAY warn about a member it does not know. It MUST NOT refuse a record for one.
3. The schemas say which members they know with the annotation `"x-eik-closed": true` on an object; they do not refuse others. The annotation means what `unevaluatedProperties: false` would 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, by `properties`, `patternProperties` or `additionalProperties`, and unknown when none does. So where `allOf` or `$ref` combine subschemas, a member that any of them names is known, and an alternative of `anyOf` or `oneOf` adds its members only when the object meets it. It does not mean `additionalProperties: false`, which sees only the members its own subschema names. A validator that wants to warn can read the annotation so, as `tools/check.mjs` and the package `packages/openeik` do.
4. 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).
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).

*Note (informative).* Rules 1 and 2 were decided on 11 October 2026. The reference implementation meets them since Stomra pull request #778 (live since 11 October 2026, `main` `8ebedf05`): a record read from a file, a folder, a version or a snapshot keeps every member it does not know, at every depth, through the store, versions, restores, merges (field by field) and exports, and its validator lists each such member as a warning, by file and path. Before #778 it refused an unknown member of an element, of a view and of many nested objects, and dropped one of a type, a layer, a level or the project. Its commands still refuse a member they do not know (in a type, they drop it), except one that a record of the model holds and that a command carries back as it was read (Part V, 1). Files and folders that are no record are kept as kept files since #768 (Part III, 7). Rule 5 it does not meet: it refuses an element or a type of a category it does not know (its category is an enumeration), and does not carry one through a pull.

## 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

1. 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`, `com0` to `com9`, `lpt0` to `lpt9`.
2. The **extension registry**, [`registry/extensions.json`](https://openeik.org/registry/extensions.json) in this specification's repository, lists the registered prefixes, each with its registrant, a contact and a short description, and the **reserved names**: `eik` and `openeik`, every prefix that starts with `eik-` or `openeik-`, the names this specification gives to folders at the top of a model's folder (Part III, 1), and `assets`, which it plans to give one. [`registry/README.md`](https://openeik.org/registry/README.md) says how a prefix is registered; [`schemas/registry/extensions.schema.json`](https://openeik.org/schemas/0.1/registry/extensions.schema.json) checks the registry's form.
3. 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.
4. 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

1. 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 example `acme: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's `made`. The keys of an object that is a map, keyed by ids or names (a railing's `insets`, by edge id; a view's `visibility.elementOpacity`, by element id), are data, not members: a key of that form there is no extension member.
2. 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).
3. A writer MUST NOT add an extension member whose value is `null`: in a patch, `null` takes 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, `null` or not (10, rule 1). Part V, 1, rule 3 says what a command processor does with a command that would add one.
4. 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 `null` removes it (Part V, 4).
5. 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.
6. 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).
7. 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).
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 to `project/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's `app` and `made`, a proposal's `by` and each entry of its `base.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.
9. 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.

*Note (informative).* No member that a schema of this specification names has a colon in its name, and no later version will name one, so an extension member never shares its name with a member of this version or of a later one. A map's keys may hold colons (the derived ids `<element id>:edge:<n>` and `<host>:bars`, section 6, rule 6, and Part V, 8.3), which is why rule 1 leaves maps out. An extension member's lengths and coordinates are best given in millimetres and in the model's axes, as the rest of its record's are (section 3).

### 11.3 An application's files

1. 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/`).
2. 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 prefix `acme`, and `Types/` a folder of the reserved name `types`, which an application does not write into. No other comparison of names follows from this rule.

*Note (informative).* The names Part III, 1 gives to folders at the top of a model's folder include, since 30 October 2026, the discipline folders that the owners' layout of 0.2 adds, `ventilation`, `plumbing`, `electrical`, `sprinkler` and `setup` (Part III, 4.4): the registry reserves them, so that no application's folder takes a name that a reader of 0.2 reads records from.

*Note (informative).* An application's files are kept files (Part III, 7: text of at most 1 MiB; a larger or binary file is left alone), whatever they hold, records of its own kinds included: every reader keeps them, a writer of the model never removes or rewrites them, though the application that owns them may (Part III, 7.3), they travel in the single-file form's `extras` (Part III, 11), and they merge file by file (Part VI, 5, rule 5). No later version of this specification puts a file or a folder of its own at the top of a model's folder under a name that is a registered prefix, whatever its letter case; a folder whose name is no registered prefix has no such protection. Rule 2 compares names only to say which folders an application keeps out of. A record's path is exact (Part III, 3, rule 1), so `Types/architecture/wall/t.json` is a kept file, not a type; and two of a model's paths that differ only in case, `ACME/a.txt` beside `acme/b.txt` or `Eik.json` beside `eik.json`, are one where case is ignored. Such a file is no kept file (Part III, 7, rule 8): a reader leaves it alone and warns, the first of two kept files in code-unit order staying the kept one, a writer refuses to write it, and a validator warns about it in a folder and reports it as an error in the single-file form ([Security and privacy, 6.2](https://openeik.org/spec/0.4/security-and-privacy#6.2)).

### 11.4 From an extension into the core (informative)

*This section is informative: it says how this specification grows, not what an implementation does.*

1. An extension becomes part of this specification only by an RFC ([`CONTRIBUTING.md`](https://gitlab.com/openeik/spec/-/blob/main/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.
2. 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.
3. The prefix stays registered to its registrant, and the registry notes what was taken into the core, in which version and under which name.

*Note (informative).* Why a prefix on each member, and not one object per application (`"extensions": {"acme": {…}}`, as some exchange formats have). A patch replaces a member whole (Part V, 4), so an application that patched its entry in a shared object would have to send every other application's entry with it; the shared object would have to be added to every closed object of every schema, or validators would warn about it on every record; and profile members are flat (Part II, 8, rule 2), as extension members are. A prefixed member at the top of a record needs none of this: every 0.1 reader keeps it already (10.1), it is diffed and merged member by member (Part VI, 4 and 5) and patched on its own, and a validator tells it by its name. One nested in a member this specification defines goes with that member: a patch of a wall's `top` replaces `top.attach` whole, an extension member in it too, so an application that patches such a member sends back the extension members it read in it. And why an application's own files, and no record lists of its own: application records (Part II, 9.1) are the lists that this text carries for the application that wrote the model, not a pattern for new ones, while an application's files are kept by every reader (Part III, 7) and need no new rule.

*Note (informative).* This section was decided by the steward on 14 October 2026 (open questions, 19). The reference implementation keeps extension members as it keeps any member it does not know (section 10, the note), and an application's folder as kept files; its commands refuse a member they do not know in an element, a patch or a view, and drop one in a type, extension members among them (Part V, 1, the note). It writes, without a prefix, members and a value that 0.1 does not define, where a model uses them: `notches` on an element, `person` on the elements and types of its `person` category (the five `person` types of its built-in library among them), the value `clt` of a material's or a layer's `pattern`, and the members of views, detail items and detail families that section 11.5 and Part II, 14 name (`textBackground`, on a detail item and in a view's display, is 0.1's since 24 October 2026, Part II, 13.2). By the steward's decision of 30 October 2026 (open questions, 115), each joins 0.2 or becomes its extension member (section 11.5). See the open questions, section 2, row 20.

### 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 |

1. **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 `generic` element 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.
2. **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`).
3. **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.

*Note (informative).* The registry gives the prefix `stomra` to the reference implementation since 17 October 2026; its description names its members of records that OpenEik does not define. `OWN_MEMBERS` in `@openeik/tools` lists the members of the table, which its validator warns of in a model of 0.2, and of 0.1 with the proposal `adopted`; `tools/generate-schemas.mjs` keeps them out of the schemas (`OWN`).
