A field the format renders and a calc reads is drawn twice today, so
"{net} x {qty} = {calc(net * qty, 2)}" can print a net that is not the net
it multiplied. The dotted-path rule already holds one draw for a level;
these pin the same hold for a plain sibling a calc reads.
Also pinned: the hold reaches a bare token of the same name, belongs to one
expansion (each repeat iteration and each nested template draws its own),
and leaves a name no calc reads drawing every time.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An alternation picks its arms evenly, so {a|a|b} was a covert 3:1 skew — a
third spelling of what a choice's weight is for, reachable only by
experiment. The check runs over the arms as written, before a reference arm
is let through, so {..a|..a} is caught too.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
{a|a|b} skews an alternation three to one, an undocumented third way to
weight beside a choice's weight and a repeated choice entry. These pin its
rejection, and one accepted case fixes the boundary: {x}{x} is two tokens,
so it stays two independent draws.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dot rule was spelled twice — once for a category or folder, once for a
field — and covered only the dot. Both now go through checkName, which
rejects the four characters no format can name: . | ( and }.
')' stays legal: on its own it is spellable, and rejecting it would refuse a
name that works.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A dot is already rejected in a category, folder or field name. The token
grammar reserves three more characters: '|' separates a token's arms, '('
opens a function call, and '}' ends the token. A name carrying one resolves
by dot path and is advertised by List, yet no format can name it.
These pin the rejection, and one accepted case fixes the boundary: ')' on
its own is spellable, so it stays legal.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A builtin could be spelled as prep (args parsed once, at compile) or call
(args parsed per render), reaching the same closure by two routes. Nothing
enforced the "exactly one of" invariant: supplying both silently dropped
call, supplying neither compiled and then panicked inside a render, and a
test existed only to police it.
prep does everything call did, so it is now the one spelling. The zero-arg
shapes the README already names — a derivation reads the output emitted so
far, a generator reads only the rng — lift through derive and generate, so
the registry reads as that taxonomy. iban stops looking its country up per
render.
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>