sprig and prose docs classic docs
Reference› ADRs› ADR008 RelationshipChainUniqueness

ADR008 RelationshipChainUniqueness

Title: Relationship chain uniqueness

  • Status: Accepted
  • Decided: D2026-08-27
  • Introduces: SP025 RelationshipAlreadyDeclaredInChainError
  • Affects: Dimension, Parser, Relationship

Context

A relationship assertion names an edge with a bare identifier. Lookup already walks the subject's dimension, then ancestor folders, then the target. That fallback is how an empty crop can still say isGrownIn { Field } when the pair lives on the universe.

The same walk made it legal to declare the edge twice: once on a folder, again on a descendant or on a dimension hanging off that folder.

relationship shouldWork { }

dimension example {
  relationship shouldWork { }
}

concept ConceptName {
  relationships {
    shouldWork { example }
  }
}

ConceptName has no dimension. ADR 003 binds this particular sentence to example.shouldWork because the target is that dimension. Point the same word at a bare concept and fallback picks the folder's copy. Two definitions, one word, ranking decides. SP021 already refuses that class of surprise for dimension names.

August tests encoded the ranking as a feature: a concept's own dimension outranked the same-named edge on an outer folder.

Decision

An edge name may not sit on a folder and also on that folder's descendants, including dimensions hanging off those folders. The inner declaration is SP025. Identical copies still error: two ids for one word is the bug even when the pair spelling matches.

Sibling dimensions may still share a word. They are not fallback for each other. Two kinds in one folder chain may each declare follows. Relating across those kinds is still ADR 003 / SP023.

Consequences

Authoring. Keep the folder declaration or the nested one, not both. A universe-level shouldWork and example.shouldWork cannot coexist.

Cost. Universes that compiled by letting an inner declaration quietly win now error. The August "outranks outer scope" cases are those universes.

What this does not do. It does not make edge names unique in the whole universe. It does not treat one dimension as occupying a name for another dimension. It does not change how an undeclared edge falls through a dimension to an ancestor folder.

Alternatives considered

Universe-wide unique edge names. Rejected. ADR 003's two dimensions sharing standsFor and isRepresentedBy, and two kinds in one chain each having follows, would become illegal. Those are not shadowing.

Error at the assertion, not the declaration. Rejected. The two definitions look fine until someone happens to write an edge that can see both. SP021 reports the inner declaration for the same reason.

Allow identical pairs, error only when inverses differ. Rejected. The manifest would still have two ids for one word, and which id an assertion bound would still depend on who it pointed at.

Evidence

examples/dimension-lookup compiled with both DimensionLookup.shouldWork and DimensionLookup.example.shouldWork in the manifest. The assertion toward example bound the dimension copy. The folder copy remained, unused by that sentence, ready to be selected by a different to.