A renderEdge label can be an operand name, and both messages printed it as
{name} — so an author was told a token renders the level when no such token
existed in the format. An edge now carries whether its label is an operand and
names itself the way boundReaders does, so both fences speak one language.
The narrowed operand cover fixed the other half: the render closure pulled the
other operand into the cover, which could report the two sides the wrong way
round ({n} renders "m" where it is m that reaches n).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The one-spelling section described a calc operand only as a route that
renders a level, so a reader could not predict that {..cat.net} beside
{calc(net * 2, 2)} is now a load error with no path token in sight. It says
so, and says what an operand's draw covers and what it leaves free.
Dropped the paragraph in checkBoundLevelsHeld that restated its own first
line and the loop below it, and the escape-analysis note in expand.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The fence now asks the right question for each kind of hold. A path head keeps
containment, since a path may read into anything the level carries. An operand
head takes the render closure, because a calc renders its field and that draw
fixes only the value the render produces.
That stops rejecting a reference to a sibling the operand never renders, which
had loaded fine before the fence widened, and starts rejecting a reference
that reaches what the operand renders through. A head whose cover is empty —
a fixed string — skips the walk.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A name starting with ".." also starts with ".", so loadDir's hidden-entry
skip reaches it first and neither isRef branch could ever fire. Removed, and
the comment in named now names the skip that actually holds the invariant it
depends on.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
checkBoundLevelsHeld already performed the right walk; it iterated the wrong
set. Walking t.held covers both kinds of hold — the level a dotted token
reads and the sibling a calc reads — so the guarantee the README states now
holds for every spelling of the operand, not just the bare one.
The path message is unchanged. A calc-only head gets its own, naming the
sibling spelling that reads the held draw.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
authored and named differed only in whether a "..path" binding was skipped,
which made two spellings of one question: what does this node contain? They
could never disagree — loadDir rejects a category or folder carrying the
prefix, so a group's children are never bindings, and TestReferenceErrors
pins both rejections.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>