sprig and prose docs classic docs
Reference› ADRs› ADR006 DimensionsInRelationships

ADR006 DimensionsInRelationships

Title: Dimension relationship endpoints

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.