sprig and prose docs classic docs
Reference› ADRs› ADR007 RelatesEndpointDimensionPairs

ADR007 RelatesEndpointDimensionPairs

Title: Relates endpoint dimension pairs

  • Status: Accepted
  • Decided: D2026-08-27
  • Revises: ADR003 EndpointDimensionPairs
  • Revised by: ADR024 RelatesTargetsInPair
  • Affects: Dimension, Parser, Relates, Relationship
  • Pairings:
    • ADR003 EndpointDimensionPairs — the relates exception
    • ADR024 RelatesTargetsInPair — the neither-endpoint fallback

Context

ADR 003 taught the compiler to look an authored edge up on both endpoint dimensions. That is how ApplicationName can write isRepresentedBy { App } when abbreviation declared the pair and application did not.

A relates body can name the same edges, but ADR 003 left relates unchanged. A pairing has no dimension of its own, and its two endpoints may belong to different ones, so that release had no subject to resolve from. The lookup walked the target concept, then ancestors, then the relates' parent chain. It never asked either endpoint's dimension.

This failed with SP015 whenever the pair lived only on a dimension:

dimension example {
  relationship shouldWork { }
}

example ExampleName { }
concept ConceptName { }

relates ConceptName and ExampleName {
  relationships {
    shouldWork { ExampleName }
  }
}

The same sentence written on ConceptName already compiled. The relates form is the one that can carry a pair describe, so authors who needed that sentence could not use the vocabulary the dimension had already declared.

Decision

A relates relationship name uses the same pair lookup as a concept assertion.

The endpoint that is not to is the subject. to is the target. Look the edge up on both of those resolved dimensions, then the existing ancestor fallback.

  • relates A and B with shouldWork { B } is A asserting toward B.
  • relates B and A with shouldWork { B } is still A asserting toward B. Clause order does not pick the subject.
  • A to that names neither endpoint treats left as subject so the target dimension is still consulted.
  • Conflicting inverses on the two endpoint dimensions are SP023, as in ADR 003.

to still resolves from the relates declaration's parent. Nested target names do not start resolving from the subject's parents.

Consequences

Authoring. A relates may use a pair declared on either endpoint's dimension. The example above compiles. The inverse shouldWork { ConceptName } compiles too, because the subject is then ExampleName.

Cost. A relates that used to bind an ancestor definition will now SP023 if the two endpoint dimensions define that edge with different inverses. That is the same cost ADR 003 accepted for concepts.

What this does not do. It does not give a relates a dimension of its own. It does not search ancestor dimensions beyond each endpoint's resolved dimension. It does not change how to is resolved.

Alternatives considered

Keep the relates-only walk. Rejected. The dimension already declared the vocabulary; failing SP015 on the form that can carry describe is the wrong error.

Left endpoint is always the subject. Rejected. relates A and B is an unordered pairing. Making left win would make clause order load-bearing.

Always consult both pairing dimensions, even when to is a third name. Rejected as more than the concept rule. When to is one of the two endpoints, both pairing dimensions are already the subject and target. When it is not, the concept analog is left plus to.

Evidence

The universe in examples/relates-uses-dimension compiled as a concept assertion and failed SP015 as a relates. ADR 003 skipped this case to keep that release small, and because a subject had not been chosen. The subject is now the other endpoint.