Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ADR 0002: Sockets attach, and collision is derived from geometry

Status: accepted, 2026-09-13.

Context

Two things in the first contract were metadata only. A Socket recorded a frame and nothing consumed it, so a wall of modules was placed by multiplying a bay index by a bay width and rotating the result, once per kit, in the content. Collision was worse: each showcase kit carried a hand-typed table of boxes beside its geometry, matched to it by eye, and every dimension that moved had to be retyped in two places or the proxies quietly drifted off the building.

Both are the same problem. The recipe already knows where the parts are and what shape they have, and the content was restating it.

Decision

Attachment

An Instance may name its own socket, another instance, and that instance's socket. build() derives the pose so the two socket frames meet, in dependency order, and reports unknown sockets, unknown targets, self-attachment and cycles with the path of the instance that asked. The derived pose is written into the instance, so nothing downstream has to know an attachment happened.

Sockets face outward along +Z. Attaching therefore turns the attached part half a turn about the socket's Y axis, so the two outward directions oppose: the convention of a plug and a socket, not of two frames laid on top of each other. Facing::Aligned asks for the other one, for a socket that marks a direction to continue in rather than a face to meet.

The convention pays for itself where it is used. A bay with a left socket at its origin facing -X and a right socket at its far edge facing +X, attached left to right, lands the next bay exactly one bay along with the same rotation, which is what the study kit's walls used to compute by hand.

Derived collision

An Element declares Collision::Bounds or Collision::Hull, and the mesher derives the proxy from the same evaluated solid it meshed, once per part definition and a transform per placement. The default is None, so trim, slats, lights and signage cost nothing and say so.

A proxy is convex and conservative. An opening subtracted inside an element is not subtracted from its proxy. An opening that has to be walked through is therefore the gap between elements, not a hole inside one. That is a real constraint on how a passable kit is authored, and it is the price of deriving collision from a boolean result without a decomposition step.

Consequences

Content states dimensions once. A collider set cannot drift from the geometry, because there is nothing left to drift from. The showcase's old tables survive only as the fixture of the test that shows the derived set occupies what they occupied.

The proxies are conservative, which suits static architecture and does not suit anything that has to be walked into and out of through its own openings. Convex decomposition, per-element materials for collision, and a proxy that follows a cut are all still open. An attachment names an instance by ID, so composing a building out of another building's instances, as the corporate block does, has to rewrite attachment targets along with the IDs it renames.