Node instance name does not reach the graph model (viewer shows identical [SMA] boxes for named fan-in legs) #58

Closed
opened 2026-06-11 11:54:23 +02:00 by Brummel · 4 comments
Owner

Surfaced by the cycle 0031 architect drift review (node-instance naming, #56).

Cycle 0031 gave every node an instance name (default = lowercased type label,
overridable via Sma::builder().named("fast")), which is what now disambiguates
two same-type fan-in legs in param_space() (sma_cross.fast.length vs
.slow.length). But that identity does not reach the graph model: model_to_json
(crates/aura-engine/src/graph_model.rs ~70-80) emits the primitive "type" via
b.label() (the type label, e.g. "SMA") plus the integer index — never the
instance name. So the WASM viewer renders two SMA legs as identical [SMA] boxes,
even when the author named them fast/slow.

This was a deliberate scope decision in 0031 (spec line 211: the cycle does not
change the JSON model surface), not a regression — sample-model.json and the
model_to_json golden are intentionally byte-identical. But it leaves the one
surviving render unable to show the disambiguation lever the author just used.

Direction (not prescriptive): have model_to_json surface the node instance name
(alongside or instead of the type label) so the viewer can label a node fast/slow.
Relates to the legibility goals of #13 (graph render) and #37 (interactive
blueprint navigation). Strikeable; no commitment.

Surfaced by the cycle 0031 architect drift review (node-instance naming, #56). Cycle 0031 gave every node an instance name (default = lowercased type label, overridable via `Sma::builder().named("fast")`), which is what now disambiguates two same-type fan-in legs in `param_space()` (`sma_cross.fast.length` vs `.slow.length`). But that identity does **not** reach the graph model: `model_to_json` (crates/aura-engine/src/graph_model.rs ~70-80) emits the primitive `"type"` via `b.label()` (the type label, e.g. `"SMA"`) plus the integer index — never the instance name. So the WASM viewer renders two SMA legs as identical `[SMA]` boxes, even when the author named them `fast`/`slow`. This was a **deliberate scope decision** in 0031 (spec line 211: the cycle does not change the JSON model surface), not a regression — `sample-model.json` and the `model_to_json` golden are intentionally byte-identical. But it leaves the one surviving render unable to show the disambiguation lever the author just used. Direction (not prescriptive): have `model_to_json` surface the node instance name (alongside or instead of the type label) so the viewer can label a node `fast`/`slow`. Relates to the legibility goals of #13 (graph render) and #37 (interactive blueprint navigation). Strikeable; no commitment.
Brummel added the idea label 2026-06-11 11:54:23 +02:00
Author
Owner

Design reconciliation (specify, in-context entry)

Spec: 0035-node-name-in-graph-model. The fork below is still listed open on this
issue ("alongside or instead of the type label"); it was resolved in the
in-context design discussion during a /boss redirect to this issue.

  • Fork: viewer label identity (name alongside vs. instead of type) → the
    viewer box's primary label is the node instance name (node_name()),
    always
    — one consistent rule, no explicit-vs-default conditional. The type
    label (label()) moves to the secondary surface (the viewer INFO/tooltip
    panel) and keeps driving role/colour. Unnamed nodes render their default
    node_name() (lowercased type), so a lone [SMA] becomes [sma]; that
    whole-graph relabel is accepted and intended.
    Rationale: coherent with the cycle-0031 direction — param_space() addresses
    knobs by instance identity (sma_cross.fast.length), so the viewer should
    speak the same identity language the author uses to address knobs; the type is
    a property, the name is the identity.
    Provenance: user selected "Name als Label" in the orchestrator's
    AskUserQuestion (box = node_name() ausnahmslos; type → info panel) on
    2026-06-12.
## Design reconciliation (specify, in-context entry) Spec: 0035-node-name-in-graph-model. The fork below is still listed open on this issue ("alongside or instead of the type label"); it was resolved in the in-context design discussion during a /boss redirect to this issue. - **Fork: viewer label identity (name alongside vs. instead of type)** → the viewer box's **primary label is the node instance name (`node_name()`), always** — one consistent rule, no explicit-vs-default conditional. The type label (`label()`) moves to the secondary surface (the viewer INFO/tooltip panel) and keeps driving role/colour. Unnamed nodes render their default `node_name()` (lowercased type), so a lone `[SMA]` becomes `[sma]`; that whole-graph relabel is accepted and intended. Rationale: coherent with the cycle-0031 direction — `param_space()` addresses knobs by instance identity (`sma_cross.fast.length`), so the viewer should speak the same identity language the author uses to address knobs; the type is a property, the name is the identity. Provenance: user selected "Name als Label" in the orchestrator's AskUserQuestion (box = `node_name()` ausnahmslos; type → info panel) on 2026-06-12.
Author
Owner

Attempt 1 RESET — the render decision was semantically wrong (restart from here)

A first cycle (0035) for this issue was fully implemented and audited, then
git reset --hard to origin/main — all 5 commits discarded, never pushed.
Recording the failed attempt and the corrected design so the cycle can restart
in a fresh context. This comment supersedes the earlier "Design reconciliation"
comment
, which recorded the now-discarded decision.

What was attempted (and is wrong)

Ratified render rule: the viewer box's primary label = node_name(), with the
type label demoted to the INFO panel. cellLabel changed ${o.type}
${o.name || o.type}.

Why it is wrong — on every level

The parens in a box label were always the parameter signature (cellLabel's
sig, built from o.params). Moving the instance name into the head position
therefore rendered, literally, fast(length) and slow(length) in the
browser — which reads as "two functions fast and slow, each called with a
length argument."
That is wrong on every level:

  • There is one function SMA, instantiated twice with different lengths.
  • fast / slow are instance names — not function names, and not parameter
    names. SMA's parameter is length.
  • Using the instance name as a parameter qualifier (e.g. SMA(fast=2)) collapses
    the moment a node has more than one parameter: which param is 2?

Type, instance name, and params are three orthogonal slots; the attempt
conflated them (instance name stolen into the function-head slot).

Corrected design (entry: brainstorm)

Leaf label as a binding: name = TYPE(params).

  • TYPE is the head (the function), always shown, with its real params in
    parens.
  • The instance name binds the instance, as a prefix before =.
  • params: value-empty blueprint view → param names only (SMA(length));
    a bound .bind constant (cycle 0034) or a compiled/value-bearing view →
    name=value (SMA(length=2)); scales to N params (SMA(length=2, alpha=0.3)).
  • Unnamed node → no prefix, just SMA(length) (also fixes the ugly lowercased
    [sma] the attempt produced).

For the sma_cross sample the legs would read fast = SMA(length) /
slow = SMA(length), with Sub and Exposure(scale) beside them.

What was correct and should be reproduced

Only the viewer render rule was wrong. The engine half was sound and should
come back unchanged:

  • prim_record (crates/aura-engine/src/graph_model.rs) emits a "name" field
    carrying node_name() into the JSON model — the viewer needs the name in the
    model to render the binding prefix. Both goldens (engine inline model_golden
    • CLI sample-model.json) carry "name". C23/C14 hold (render/model-surface
      only; name dropped at lowering; deterministic).

Open design points for the restart

  • positional SMA(2) vs named SMA(length=2) inside the parens.
  • value-empty blueprint view vs compiled/bound view: aura graph renders the
    value-empty blueprint, so concrete values only appear for .bind constants
    (cycle 0034) or a future compiled render — SMA(length) is the max in the plain
    blueprint view.
  • how composites fit: a composite box already shows its definition name; does
    it get the same name = TYPE(...) treatment, and what are its "params"?
  • the collapsed-composite point: fast/slow live inside sma_cross, which
    renders collapsed by default — the disambiguation is only visible after
    expanding. Possibly a separate follow-up.

Note

The ascii/ascii-dag renderer was retired in cycle 0026; the browser viewer
(graph-viewer.js, served by aura graph as self-contained HTML) is the only
render surface. PrimitiveBuilder::label (the model's "type" field) is unchanged.

## Attempt 1 RESET — the render decision was semantically wrong (restart from here) A first cycle (0035) for this issue was fully implemented and audited, then **`git reset --hard` to origin/main — all 5 commits discarded, never pushed.** Recording the failed attempt and the corrected design so the cycle can restart in a fresh context. **This comment supersedes the earlier "Design reconciliation" comment**, which recorded the now-discarded decision. ### What was attempted (and is wrong) Ratified render rule: the viewer box's **primary label = `node_name()`**, with the type label demoted to the INFO panel. `cellLabel` changed `${o.type}` → `${o.name || o.type}`. ### Why it is wrong — on every level The parens in a box label were **always** the parameter signature (`cellLabel`'s `sig`, built from `o.params`). Moving the instance name into the head position therefore rendered, literally, **`fast(length)`** and **`slow(length)`** in the browser — which reads as *"two functions `fast` and `slow`, each called with a `length` argument."* That is wrong on every level: - There is **one** function `SMA`, instantiated twice with different lengths. - `fast` / `slow` are **instance names** — not function names, and not parameter names. SMA's parameter is `length`. - Using the instance name as a parameter qualifier (e.g. `SMA(fast=2)`) collapses the moment a node has more than one parameter: which param is `2`? Type, instance name, and params are **three orthogonal slots**; the attempt conflated them (instance name stolen into the function-head slot). ### Corrected design (entry: brainstorm) Leaf label as a **binding**: `name = TYPE(params)`. - `TYPE` is the head (the function), always shown, with its **real** params in parens. - The **instance name** binds the instance, as a prefix before `=`. - params: value-empty blueprint view → param **names** only (`SMA(length)`); a bound `.bind` constant (cycle 0034) or a compiled/value-bearing view → `name=value` (`SMA(length=2)`); scales to N params (`SMA(length=2, alpha=0.3)`). - Unnamed node → no prefix, just `SMA(length)` (also fixes the ugly lowercased `[sma]` the attempt produced). For the `sma_cross` sample the legs would read **`fast = SMA(length)`** / **`slow = SMA(length)`**, with `Sub` and `Exposure(scale)` beside them. ### What was correct and should be reproduced Only the viewer **render rule** was wrong. The engine half was sound and should come back unchanged: - `prim_record` (crates/aura-engine/src/graph_model.rs) emits a `"name"` field carrying `node_name()` into the JSON model — the viewer needs the name in the model to render the binding prefix. Both goldens (engine inline `model_golden` + CLI `sample-model.json`) carry `"name"`. C23/C14 hold (render/model-surface only; name dropped at lowering; deterministic). ### Open design points for the restart - positional `SMA(2)` vs named `SMA(length=2)` inside the parens. - value-empty blueprint view vs compiled/bound view: `aura graph` renders the **value-empty** blueprint, so concrete values only appear for `.bind` constants (cycle 0034) or a future compiled render — `SMA(length)` is the max in the plain blueprint view. - how **composites** fit: a composite box already shows its definition name; does it get the same `name = TYPE(...)` treatment, and what are its "params"? - the **collapsed-composite** point: `fast`/`slow` live inside `sma_cross`, which renders collapsed by default — the disambiguation is only visible after expanding. Possibly a separate follow-up. ### Note The ascii/`ascii-dag` renderer was retired in cycle 0026; the **browser viewer** (`graph-viewer.js`, served by `aura graph` as self-contained HTML) is the **only** render surface. `PrimitiveBuilder::label` (the model's `"type"` field) is unchanged.
Author
Owner

Design reconciliation (specify, in-context entry)

Spec: 0035-node-name-in-graph-model. The forks below are listed open by the
"Attempt 1 RESET" comment's "open design points for the restart"; they were
resolved in an in-context design discussion this session. This comment
supersedes that open-points list — the points below are now settled.

  • Fork: viewer label identity (name alongside vs. instead of type). The
    leaf box head renders <name>: <TYPE>[params] — the instance name as an
    "is-a" declaration prefix joined by :. The type stays the head; params stay
    in the brackets. Rejected, and why (so the restart does not relapse):
    fast(length) (attempt 1 — steals the name into the function-head slot);
    fast = SMA(length) (the RESET's own proposal — = reads as a value/result
    binding, and a node's output is a stream, so it conflates instance-identity
    with output-value).
    Provenance: user, 2026-06-13 — "Aendere die Anzeige von 'SMA[length]' zu
    'fast: SMA[length]'"; and, rejecting =, "Aber 'fast = SMA(length)' sieht
    so aus, als ob 'fast' das RESULT der Funktion ist!".

  • Fork: which nodes carry the prefix. Only explicitly .named()
    nodes. An unnamed node keeps the bare TYPE[params]; node_name()'s
    lowercased-type default is not treated as "a name present" (it would render
    noise like sub: Sub, a redundant type-duplicate).
    Provenance: user, 2026-06-13 — "Wenn ein Name da ist, soll er so angezeigt
    werden." (read as: an explicit name; flagged to the user as explicit-only,
    who proceeded to spec on that framing).

  • Fork: params shown as names or values. Param names (SMA[length]),
    because aura graph renders the value-empty blueprint. Unchanged behaviour.
    Provenance: established behaviour + this session's discussion.

  • Settled separately: square-bracket param signature TYPE[...] already
    shipped (commit a051571).

  • Out of scope (separate follow-up): collapsed-composite visibility —
    fast/slow live inside sma_cross, which renders collapsed by default, so
    the prefix is only visible after expanding the composite. This cycle does not
    change default expansion; it specifies the leaf render + the model surface.

## Design reconciliation (specify, in-context entry) Spec: 0035-node-name-in-graph-model. The forks below are listed open by the "Attempt 1 RESET" comment's "open design points for the restart"; they were resolved in an in-context design discussion this session. This comment **supersedes** that open-points list — the points below are now settled. - **Fork: viewer label identity (name alongside vs. instead of type).** The leaf box head renders `<name>: <TYPE>[params]` — the instance name as an "is-a" declaration prefix joined by `:`. The type stays the head; params stay in the brackets. Rejected, and why (so the restart does not relapse): `fast(length)` (attempt 1 — steals the name into the function-head slot); `fast = SMA(length)` (the RESET's own proposal — `=` reads as a value/result binding, and a node's output *is* a stream, so it conflates instance-identity with output-value). Provenance: user, 2026-06-13 — "Aendere die Anzeige von 'SMA[length]' zu 'fast: SMA[length]'"; and, rejecting `=`, "Aber 'fast = SMA(length)' sieht so aus, als ob 'fast' das RESULT der Funktion ist!". - **Fork: which nodes carry the prefix.** Only **explicitly `.named()`** nodes. An unnamed node keeps the bare `TYPE[params]`; `node_name()`'s lowercased-type default is not treated as "a name present" (it would render noise like `sub: Sub`, a redundant type-duplicate). Provenance: user, 2026-06-13 — "Wenn ein Name da ist, soll er so angezeigt werden." (read as: an explicit name; flagged to the user as explicit-only, who proceeded to spec on that framing). - **Fork: params shown as names or values.** Param **names** (`SMA[length]`), because `aura graph` renders the value-empty blueprint. Unchanged behaviour. Provenance: established behaviour + this session's discussion. - **Settled separately:** square-bracket param signature `TYPE[...]` already shipped (commit a051571). - **Out of scope (separate follow-up):** collapsed-composite visibility — `fast`/`slow` live inside `sma_cross`, which renders collapsed by default, so the prefix is only visible after expanding the composite. This cycle does not change default expansion; it specifies the leaf render + the model surface.
Author
Owner

Design reconciliation (specify, in-context entry) — addendum

Spec: 0035-node-name-in-graph-model. This supersedes the provenance of two
points the prior reconciliation comment recorded with an orchestrator reading;
the user has now decided them explicitly in-session (2026-06-13), with the full
thread history (including the earlier opposite choice) in front of them.

  • Fork: which nodes carry the prefix → only explicitly .named() nodes.
    An unnamed node keeps the bare TYPE[params]; node_name()'s lowercased-type
    default is NOT surfaced (it would be redundant noise — sub: Sub — once the
    type is already the box head).
    Provenance: user, 2026-06-13. The user was shown that they had earlier chosen
    the opposite (surface-the-default, whole-graph relabel [SMA][sma]) in the
    now-superseded comment 1042 — but that choice was bound to the old
    name-replaces-type notation, which the RESET (comment 1043) discarded.
    Under the new name: TYPE[params] prefix notation the user confirmed
    explicit-only: "Korrekt." (agreeing the default prefix is redundant once the
    type is the head), then "alle vier wie empfohlen" (point 1 = explicit-only).

  • Sub-decision: named() non-empty rule → kept; its doc re-grounded in 0035.
    The non-empty debug_assert! predates this cycle (written for the old notation
    where an empty name meant an empty box). The code is unchanged; the new
    rationale: an explicit name is load-bearing on two surfaces — a knob-address
    segment (sma_cross.<name>.length, via node_name() in collect_params) and
    the Some/None switch for the model "name" field + viewer prefix — so
    Some("") is forbidden at source.
    Provenance: user, 2026-06-13 — "alle vier wie empfohlen" (point 2 = keep rule +
    re-ground doc in 0035; point 3 = keep debug_assert!, do not harden to
    assert!).

  • Spun off (NOT this cycle): parameter ganging (one knob driving several
    nodes) as an explicit composite-exposed shared param — filed as a separate
    idea issue. Explicitly NOT via name collision: param_space() is injective by
    design (C12/C19; check_param_namespace_injectiveDuplicateParamPath), so a
    shared address is a hard error, not a shared knob.

## Design reconciliation (specify, in-context entry) — addendum Spec: 0035-node-name-in-graph-model. This **supersedes the provenance** of two points the prior reconciliation comment recorded with an orchestrator *reading*; the user has now decided them explicitly in-session (2026-06-13), with the full thread history (including the earlier opposite choice) in front of them. - **Fork: which nodes carry the prefix** → only explicitly `.named()` nodes. An unnamed node keeps the bare `TYPE[params]`; `node_name()`'s lowercased-type default is NOT surfaced (it would be redundant noise — `sub: Sub` — once the type is already the box head). Provenance: user, 2026-06-13. The user was shown that they had *earlier* chosen the opposite (surface-the-default, whole-graph relabel `[SMA]`→`[sma]`) in the now-superseded comment 1042 — but that choice was bound to the old **name-replaces-type** notation, which the RESET (comment 1043) discarded. Under the new `name: TYPE[params]` **prefix** notation the user confirmed explicit-only: "Korrekt." (agreeing the default prefix is redundant once the type is the head), then "alle vier wie empfohlen" (point 1 = explicit-only). - **Sub-decision: `named()` non-empty rule** → kept; its doc re-grounded in 0035. The non-empty `debug_assert!` predates this cycle (written for the old notation where an empty name meant an empty box). The **code is unchanged**; the new rationale: an explicit name is load-bearing on two surfaces — a knob-address segment (`sma_cross.<name>.length`, via `node_name()` in `collect_params`) and the `Some`/`None` switch for the model `"name"` field + viewer prefix — so `Some("")` is forbidden at source. Provenance: user, 2026-06-13 — "alle vier wie empfohlen" (point 2 = keep rule + re-ground doc in 0035; point 3 = keep `debug_assert!`, do not harden to `assert!`). - **Spun off (NOT this cycle):** parameter ganging (one knob driving several nodes) as an explicit composite-exposed shared param — filed as a separate `idea` issue. Explicitly NOT via name collision: `param_space()` is injective by design (C12/C19; `check_param_namespace_injective` → `DuplicateParamPath`), so a shared address is a hard error, not a shared knob.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#58