ADR006 DimensionsInRelationships
Title: Dimension relationship endpoints
- Status: Accepted
- Decided: D2026-08-25
- Introduces: SP024
AmbiguousEndpointError - Affects:
Dimension,Parser,Relates,Relationships,Ui
Context
A relationship target is resolved by walking the concept tree. Dimensions live in a parallel map on the same scope, so this failed with SP007:
dimension vendor { }
vendor Vendor1 { }
vendor Vendor2 { }
concept ConceptName {
relationships {
isAssociatedWith { vendor }
}
}
The authoring workaround was to enumerate every member (Vendor1, Vendor2).
That is a different sentence — it names those concepts, not the kind — and it
goes stale when a member is added later.
relates ConceptName and vendor failed the same way. The parser already accepts
both forms; a dimension body already allows a relationships { } instance
block. The compiler dropped dimension-authored instance edges, and the
finalizer never mirrored an inverse onto a dimension.
Two readings of "relate to the dimension" were in play: the dimension as a node (one fact about the kind), and quantification over members (the compiler or UI invents edges to Vendor1 and Vendor2, including ones added later).
Decision
A relationship endpoint may be a concept or a dimension. That is one fact about the kind. Members are not implied.
Resolution. Reuse the existing path rule (ADR 002). At the last hop, look
for a concept first, then a dimension. If both exist in that scope, SP024.
If neither does, SP007. Do not turn allowDimensions on for every hop.
Pair lookup. A dimension endpoint is its own dimension. ADR 003 still applies: look the edge up on both endpoint dimensions.
Authoring. Both relationships { } and relates may name a dimension.
relates remains the only place a pair can carry its own describe. A
dimension may assert instance edges from its own relationships { } block.
Manifest. Dimensions keep relationships as definition ids. Instance
adjacency is a new edges array on the dimension, same shape as a concept's
relationships entries. Inverse rehydration writes onto that array, never
onto members.
UI (beta). A dimension target is a door to the kind page. The kind page keeps members as they are and shows connections (and pairings) separately. Vendor1 does not mention ConceptName.
Consequences
Authoring. isAssociatedWith { vendor } and isAssociatedWith { Vendor1 }
are both legal and mean different things. The first does not go stale when
Vendor3 is added. The second still names a particular vendor.
Cost. A scope that already has both concept vendor and dimension vendor
cannot use the bare name as an endpoint; that is now SP024 rather than a
quiet bind to the concept. Dimension ids in to are a new shape for
manifest consumers. The old UI is not updated.
What this does not do. It does not expand the fact onto members. It does
not add a dimension keyword in references. It does not change nested
dimension visibility (ADR 004). Dimension–dimension pairings are neither
featured nor forbidden; the resolver will accept them.
Alternatives considered
Compile-time expansion. isAssociatedWith { vendor } would emit edges to
every current member. Rejected: the manifest would invent structure the
author did not write, and consumers could not tell a kind-level fact from an
enumeration.
Show the edge on every member page. Rejected for v1. ConceptName → vendor is one hop; Vendor1 is a second hop.
Explicit syntax (isAssociatedWith { dimension vendor }). Rejected. The
identifiers already parse; a new production would grow the language to
disambiguate a case SP024 can refuse.
Quantification syntax (all vendor). Rejected as the meaning of a bare
dimension name. If a later record wants a covering-the-members sentence, it
should be a different form.
Enable allowDimensions on every path hop. Rejected. That flag was left
for nested dimensions (ADR 004, deferred). Preferring a dimension at every
segment would change existing resolution.
Evidence
The work universe that asserted isAssociatedWith { vendor } was SP007
before this change. Enumerating Vendor1 and Vendor2 compiled and meant
the wrong thing. The parser already accepted instance relationships on a
dimension body; those edges never appeared in the manifest.