Graph viewer: render bound params (e.g. weights[2]=0.5) instead of dropping them from the node signature #63
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Symptom (observable)
The
aura graphviewer renders the cycle-0036 sample'sblendnode asbut that node is a
LinComb(3)with three input ports (term[0..2]), whosethird weight was
bind-bound to a constant (weights[2] = 0.5). So the renderedsignature claims arity 2 while the box shows 3 input ports, and the binding — the
fixed
0.5— is invisible. The signature misrepresents the node.The bound param correctly drops out of the sweepable surface (that is what
bindis for). The defect is that it also vanishes from the rendered signature,which should still show all slots and mark the bound one as a fixed constant.
Root cause (where the information is lost)
PrimitiveBuilder::bind(crates/aura-core/src/node.rs:149) does two things:self.schema.params.remove(pos)— removes the param from the schema, andbuildclosure (full.insert(pos, value)).prim_record(crates/aura-engine/src/graph_model.rs:72-89) emits onlyb.schema().params, so a bound param leaves no trace in the graph model: notits name, not its kind, not its value. The model serializer never builds the
node, so the value captured in the closure is unreachable to it. Net: for the
blend the model carries
ins= 3 ports butparams= 2, and no record of thebinding. The viewer's
cellLabel(crates/aura-cli/assets/graph-viewer.js,the
sigbuilt fromo.params) therefore can only show 2 weights.This is a data gap, not a label bug — the fix must carry the bound info into the
model first.
Conceptual fix
Separate the two concerns
bindcurrently conflates:param_space, the sweep axes, C12/C19):bindremovesthe slot — correct, keep it.
bindshould annotate, not erase. Show allslots; mark the bound one with its fixed value.
This is consistent with the value-empty blueprint convention: a free param
renders as a name (its value is deferred to sweep/bootstrap — e.g.
SMA[length]),while a bound param has its value fixed at blueprint time, so it renders as
name=value. Showing the bound value is showing a structural fact, not a tuningvalue.
Proposed fix — structurally identical to cycle 0035
Cycle 0035 (commit
0ae8320,feat(aura): surface explicit node instance name in the graph viewer) threaded a conditional"name"field throughengine -> model -> viewer + goldens + a render guard. This is the same shape:
node.rs):bindadditionally records a visiblebound: Vec<(String /*slot*/, ScalarKind, Scalar /*value*/)>(alongside theexisting closure capture), plus a
bound_params()accessor — the twin ofinstance_name()(node.rs:120). The closure mechanics stay unchanged.graph_model.rsprim_record): emit a conditional"bound"field only when non-empty, e.g.
"bound":[["weights[2]","f64","0.5"]]. Deterministic (C14): theScalarvalue formats to a canonical string (mirror the existing
kind_str/named_kindhelpers; pick a fixed f64/i64/bool/timestamp rendering).adaptNodescarriesbound;cellLabelmerges the freeparams and the bound params back into slot order and renders a bound slot as
weights[2]=0.5, visually dimmed (reuse the dim-font pattern the 0035:separator already uses).
crates/aura-cli/tests/fixtures/sample-model.json(the blend node gains a
"bound"field); add aprim_recordunit test (boundfield present only when bound) and a headless viewer render guard (bound slot
renders
name=value, dimmed; free slots unchanged).C23 is unaffected:
bindstill resolves the name to a fixed position and thecompilat is unchanged; this only adds a render/debug-symbol surface (like the
instance name), dropped at lowering.
The one open design decision — settle BEFORE implementing
The render notation. Recommended (keeps the existing
TYPE[...]convention fromSMA[length]):Alternatives considered:
LinComb(A, B, C=0.5)— switches the bracket to(); that is a globalnotation change for every node's render, a bigger decision than this issue.
LinComb[weights[0], weights[1], 0.5]— positional; terser but drops the slotname on the bound entry.
Decide the bracket style and the bound annotation (with the user) before writing
the spec. Everything else above is settled (the engine+viewer mechanism mirrors
0035).
Grounding (currently-green tests that ratify the mechanics this builds on)
bindmechanics:bind_removes_slot_and_reconstructs_positionally,bind_kind_mismatch_panics,bind_unknown_slot_panics(crates/aura-core/src/node.rs).LinCombarity + named slots:chained_bind_reconstructs_positional_vector,input_slots_are_named_term_index(crates/aura-std/src/lincomb.rs).prim_record_emits_name_for_explicit_only(crates/aura-engine/src/graph_model.rs)viewer_name_prefixguard (crates/aura-cli/tests/).blendnode insignals()(crates/aura-cli/src/main.rs), cycle 0036.Meta
bindalready works; this adds a model+renderrepresentation for it.
(interactive blueprint navigation / viewer).
extension with one design decision (the notation). Settle the notation first
(a short design call), then it is a 0035-shaped, test-specifiable engine+viewer
change — not a blind RED-first debug.
Design reconciliation (specify, in-context entry)
Spec: 0037-bound-param-in-graph-model. The fork below is still listed open on
this issue ("The one open design decision — settle BEFORE implementing"); it was
resolved in the in-context design discussion of this
/bosssession.square-bracket
TYPE[...]convention (asSMA[length]) and render the boundslot as
name=value(e.g.weights[2]=0.5), visually dimmed (reuse the 0035:instance-name separator's muted font). The two alternatives the issuelisted were both rejected: positional value-only (
...,0.5) drops the slotname; parens (
LinComb(...)) is a global notation change beyond this issue.Provenance: the user selected this option directly in the
/boss #63sessionon 2026-06-13, in response to the notation decision the issue flagged for the
user ("Decide the bracket style and the bound annotation (with the user)
before implementing").