Openings
One hole in one wall, and every drawing reads the same hole.
An opening is three answers: which host, what kind, and where along that host's run. The host is usually a wall; a pitched roof can also carry one — see a roof is also a host below.
{"t":"opening","id":"front","on":"south","kind":"door","width":0.95,"room":"living"}
{"t":"opening","id":"window","on":"north","kind":"window","at":"third","width":1.8,"sill":0.9,"head":2.3}You never say where the hole is in the building — only where it is along its host. The wall already knows where it runs, so the opening does not need to, and the two can never disagree about it.
Placing it
Leave at off and the opening centres itself. That is the right answer more
often than people expect, and it is the one that survives the wall changing
length.
When centre is wrong, say where in words: start, end, third,
two-thirds, center. These state intent, so they still mean what you meant
after the building moves.
A numeric at is where the hole starts, not where it is centred.
{"at":4,"width":2} occupies 4.0 to 6.0 m along the run — not 3.0 to 5.0.
This is the one field in the format that will do something perfectly reasonable and not what you meant: no error, no warning, a door a metre from where you put it. Reach for a named position unless an exact axis coordinate is genuinely the point.
Which rooms it serves
An exterior door names one room: "room":"living".
An interior opening names two, in order:
"between":["kitchen","living"] — from the first, swinging into the second.
That ordered pair is doing two jobs at once. It builds the connectivity graph
the reachability and egress checks walk, and it decides which way the leaf
swings on the plan. Get the order backwards and the drawing shows a door
opening into the room you meant to leave.
What the compiler does with it
It cuts the span [sill, head] out of a one-dimensional wall run and keeps
what is left: the pieces before, below, above and after the hole.
Every drawing then consumes those same pieces. Nothing re-derives the window per view, which is why it cannot be a gap in plan and solid wall in section.
A roof is also a host
on may name a pitched roof — gable, hip or shed — and the opening
becomes a skylight:
{"t":"opening","id":"sky","on":"roof-main","kind":"window","width":1.2,"at":"third","up":1.4}kind must be window; a door on a roof is a schema error, not a hatch. at
places it along the ridge axis exactly the way it places along a wall, and
up is the new axis a slope adds — metres up the plane from the eave to the
sill edge, centred between eave and ridge when omitted. It draws where a
drafter would draw it: on the roof plan, dashed overhead on the floor plan, as
a gap with its glazing bar where a section cuts it, and on the roof face in
the elevation that sees the slope.
A dormer is more than a hole, so it is its own entity rather than an
opening variant: {"t":"dormer","on":"roof-main","at":6,"width":2.2} compiles
cheek walls, a perpendicular gable mini-roof and one face window from the
machinery the rest of the model already uses. A dormer or skylight that leaves
its host plane — past the ridge, past the eave, off the rectangle — is a
blocking error, and flat roofs and formdef roofs host neither.
What bites
kind decides which operation values are legal. A door is swing,
sliding, overhead or double-acting. fixed belongs to a window. A cased
opening with no leaf at all is not a door with the leaf removed — it is
kind:"opening".
An opening wider than its wall is caught, and it is blocking. So is one hosted on a wall that does not exist. Both name the host in the message.
between is probed against the geometry. If the wall at that point does not
actually stand between the two rooms you named, the check fails rather than
trusting the label. It is not being difficult — a connectivity graph built from
wishes is worth nothing.