ADR002 NestedNameResolution
Title: Nested name resolution
- Status: Accepted
- Decided: D2026-08-21
- Affects:
Concept,Parser,Relationships
Context
In January 2026, while prose still had a fixed ontology (series, book,
chapter), names walked the container chain: current container, then the parent,
then outward to the universe. First match won. That rule was written down on the
names-and-scoping reference page and implemented as resolveNameInScope in the
old graph builder.
In March the compiler was rewritten around in-clause placement. A name became
a path: concatenate it onto the enclosing container, or treat it as absolute when
the first segment is the universe, or re-anchor when the first segment equals an
ancestor name. Relationship targets reused that placement helper. The live docs
kept saying "names resolve locally first; if not found, prose looks upward,"
which describes January, not the compiler that shipped.
The question is whether to restore the January walk for references. Nested
universes make that expensive to get wrong: a short name that is missing locally
can bind to a distant Well, App, or Item at universe scope. Same-level
ambiguity does not catch that. The dangerous case is one outer match the
author was not thinking about.
Decision
Keep the March path rule. A bare name is a child of the enclosing container. A path may start at the universe (absolute) or at an ancestor name (re-anchor). Qualification is how you leave the folder.
From Fizz, Foo.Buzz resolves because Foo is an ancestor:
universe Universe { }
relationship a and b { }
concept Foo {
concept Bar {
concept Fizz {
relationships {
a { Foo.Buzz }
}
}
}
concept Buzz { }
}
Bare Buzz means Foo.Bar.Buzz and fails. Same-parent siblings still resolve
with a short name: GrindFlour may write Flour when both sit on the universe.
Consequences
Cost. Uncle references need a path (Foo.Buzz, or Universe.ApplicationName
from a nested vocabulary). Nested folders are more verbose than January.
Safety. A missing local name is an error, not a surprise bind. Nested containers stay namespaces. Nesting remains the tree; relationships remain the graph; a path is the explicit way out.
Docs. The live Scope section must describe this rule. "Looks upward" is not what the compiler does.
What this does not freeze. The compiler's resolveScopePath helper is
unchanged. This record is the language rule, not a new implementation.
Alternatives considered
Restore January's bare ancestor-sibling lookup. Buzz from Fizz would find
Foo.Buzz without naming Foo. Rejected for now, for the second time. The
failure mode is an unintended outer bind in a large universe, not two matches at
the same level. Same-parent siblings already work without a walk. We are not
opposed to adding that lookup later if authoring pain shows up in real universes.
This record is "not now," not "never."
Universe-wide unique-name fallback. If exactly one Buzz exists anywhere,
bind it. Rejected. It makes nesting irrelevant and turns every new homonym into a
breakage.
Evidence
The re-anchor rule is the one authors already use when they write Foo.Buzz
rather than a fully qualified Universe.Foo.Buzz. The failing case people trip
on is the bare uncle (Buzz, or ApplicationName from inside
CommonAbbreviations), which is exactly the bind this decision refuses to make
quietly.