feat(aura-core,aura-engine,aura-cli): node-instance naming retires ParamAlias

Every blueprint node now carries a name. A primitive builder gains an optional
instance name (Sma::builder().named("fast")); when omitted it defaults to the
node's type label, ASCII-lowercased verbatim ("SMA"->"sma", "SimBroker"->
"simbroker"). param_space() is now uniformly <node>.<param> at every level
including the root (sma_cross.fast.length, exposure.scale). Fan-in
distinguishability (C9) and signature_of re-key onto the node name: two same-type
siblings both defaulting to "sma" collide and IndistinguishableFanIn fires, so
one act -- naming the legs "fast"/"slow" -- fixes both the naming collision and
the fan-in check. The index-addressed ParamAlias overlay, Composite.params, the
Composite::new 5th argument, aliases_on, and check_alias_indices are removed
(Role.name and OutField.name untouched). Ledger C9 and C23 amended.

This is why the change is correct, not merely convenient: blueprints are
value-empty (C11), so the thing that would otherwise distinguish two SMAs --
their length -- is not present at construction; identity must come from a name,
not a deferred value. Closes the #56 friction surfaced by the param-space &
sweep milestone fieldtest (a freshly-authored 2-SMA cross was unbindable and
would not bootstrap without two hand-counted ParamAlias overlays).

Two plan defects were corrected during implementation and verified:
- the three new fan-in/path tests authored sma_cross at root level, which would
  qualify to "fast.length" (root prefix is empty) and is not the nested case the
  fan-in check inspects; nesting sma_cross under a root (a shared
  sma_cross_under_root helper) restores the asserted "sma_cross.fast.length" and
  IndistinguishableFanIn{node:2};
- three cycle-0030 named-binder tests bound the real harness by the old names
  (sma_cross.fast / scale) and were migrated to the new path strings, surfaced by
  the workspace test gate.

Verified by the orchestrator: cargo build/test --workspace green (198 tests,
0 red), clippy --all-targets -D warnings clean. model_to_json emits the type
label + factory param names (node-name-independent), so sample-model.json and the
graph_model golden are byte-identical (untouched). NodeSchema gained a Default
derive (the builder default-name test constructs an empty schema). fieldtests/
are frozen non-workspace records, not migrated.

closes #56
This commit is contained in:
2026-06-11 11:51:30 +02:00
parent d8900900b5
commit ffed8cc612
8 changed files with 298 additions and 455 deletions
+27 -24
View File
@@ -349,30 +349,33 @@ its producing node as a `name := …` binding; originally `[out:<name>]` markers
but are dropped at lowering — `ItemLowering::Composite.output` is `Vec<(usize,
usize)>`, raw index pairs only, so the compilat is name-free (verified: the
compiled-view render stayed bit-identical across this change).
**Realization (cycle 0019 — name the composite boundary, #41).** The same
named-projection shape now covers the other two boundary edge-kinds, so **all
three** are uniform: `input_roles` is a `Vec<Role { name, targets }>` (was a bare
`Vec<Vec<Target>>`) and a composite carries `params: Vec<ParamAlias { name, node,
slot }>`, alongside `output: Vec<OutField>`. Each is an ordered, positionally-indexed
**named projection** of interior handles. Param aliasing is a **pure naming overlay**,
not curation: `param_space()` relabels an aliased slot's surface name **in place**
slot order, arity, and kinds are unchanged, so the sweep surface is identical and the
`param_space_mirrors_compiled_flat_node_param_order` anchor stays green. Like the
output names, role and param **names** are **non-load-bearing** (C23): they live at
the blueprint boundary and in render (`[in:<name>]` / `[param:<name>]` markers) but
are dropped at lowering — the compilat is wired by raw index and a dangling alias is
rejected at compile (`BadInteriorIndex`), not silently lowered. The full composite
boundary signature (named inputs, params, multi-outputs) is now legible without
changing the compilat.
**Refinement (fan-in distinguishability, 2026-06-08).** A fan-in node (>1 input)
is well-formed only if its colliding sources — sources with equal recursive
signatures (type initial + alias initials + recursive input signatures) — do not
hide an unnamed configuration axis: a collision is a `CompileError`
(`IndistinguishableFanIn`) when at least one colliding source carries an
unaliased param slot. Genuinely-interchangeable sources (equal signatures, no
param) stay legal. Construction-phase only; the compilat stays name-free (C23).
The graph view renders each fan-in input as the shortest sibling-unique prefix
of its source signature.
**Realization (cycle 0019 — name the composite boundary, #41; param-overlay retired,
cycle 0031).** The named-projection shape covers the surviving boundary edge-kinds:
`input_roles` is a `Vec<Role { name, targets }>` (was a bare `Vec<Vec<Target>>`),
alongside `output: Vec<OutField>`. Each is an ordered, positionally-indexed
**named projection** of interior handles. **Param projection is no longer a
composite overlay** (the index-addressed `ParamAlias` was retired in cycle 0031):
a node's surface param name flows from its own **instance name** — every node
carries a name (default = its lowercased type label, override via `.named()`), and
`param_space()` is uniformly `<node>.<param>` at every level including the root. A
same-type fan-in is distinguished by naming the colliding legs, the same single act
that qualifies their param paths. Like the output and role names, **node names are
non-load-bearing** (C23): they live at the blueprint boundary and in render but are
dropped at lowering — the compilat is wired by raw index. The full composite
boundary signature (named inputs, multi-outputs) and the per-node param path are
legible without changing the compilat.
**Refinement (fan-in distinguishability, 2026-06-08; node-name keying, cycle 0031).**
A fan-in node (>1 input) is well-formed only if its colliding sources — sources
with equal recursive **node-name** signatures (each node contributes its name —
the instance name, default = the lowercased type label — then its inputs'
signatures in slot order, stopping at a named source) — do not hide an unnamed
configuration axis: a collision is a `CompileError` (`IndistinguishableFanIn`)
when at least one colliding source carries a **param** (a param-bearing same-name
collision), resolved by giving the colliding nodes distinct names. Paramless
interchangeable same-name sources (equal signatures, no param) stay legal.
Construction-phase only; the compilat stays name-free (C23). The graph view
renders each fan-in input as the shortest sibling-unique prefix of its source
signature.
### C10 — Strategy output is an intent/exposure stream; position management is a decoupled derived layer; brokers are downstream nodes
**Guarantee.** A strategy's primary, backtestable output is **not** an equity