Parameter ganging: one knob driving several nodes (explicit composite-shared param) #61
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?
Parameter ganging: one knob value driving several nodes' params at once.
Motivation. Sometimes an author wants N nodes to share a tuning param (e.g.
"all EMAs in this subgraph share one smoothing period", or a symmetric pair that
always sweeps together) rather than exposing N independent knobs.
NOT via name collision.
param_space()is injective by design (C12/C19): aduplicated path is "a knob no binding can select alone" —
check_param_namespace_injectiverejects it withDuplicateParamPathbefore anybinding resolves. Two nodes sharing a
node_name()(or both empty) is a harderror, not a shared knob — and intentionally so: it keeps every axis independently
sweepable and stops a forgotten name from silently coupling two nodes.
Direction (not prescriptive). Make ganging an explicit composite construct:
a composite exposes its own param and binds it into several child slots, keeping
ONE injective address (
sma_cross.length) that fans out 1:N at a controlled,declared site. Sibling to
bind(0034, which fixes a child param as a constant);here a composite param is shared across children. Identity and shared-knob stay
orthogonal — distinct boxes (
fast/slow) can share a param without mergingidentity.
context: surfaced during the 0035 (#58) spec design — the non-empty-name question
led to "could empty/colliding names map one value onto both SMAs?". Answer: no, by
the injectivity invariant; ganging belongs as an explicit composite feature.
Strikeable; no commitment.
Triage 2026-07-09 (tree at
68317ec) — still unbuilt, and the cost of the gap grew with the blueprint-generic verbs:--axissweep produces a full cartesian grid including asymmetric channel points no author intends; r_meanrev "gangs" its mean/var Ema lengths only by baking a closed constant (main.rs:1861).Design-discovery triage (/boss brainstorm swarm) — direction fork bounced
A four-stance design swarm (minimal-mechanism, Op-vocabulary-consistency,
author-ergonomics, determinism/invariant-safety) explored the ganging design,
each grounding its answers in cited code/ledger. Result: the mechanism is
convergent, but the direction (which tier ganging lives at) is a genuine
architectural fork that hinges on a value/semantics call, so it is deferred to
the maintainer.
Convergent — the mechanism, IF ganging is a blueprint construct
3 of 4 stances agreed on distinct cited grounds:
Op(a param gang, e.g.Gang { as_name, into: Vec<"node.slot"> }) mirroringFeed's role→N-slots fan-out — NOT overloadingExpose(output-field-only,construction.rs:282). It must land on BOTH theop-script and Rust-builder cadences in lockstep (the "no second validator"
C24 discipline,
construction.rs:1-9).bind-style closure-wrap, not a side-table: remove the Nchild slots from
param_spaceand wrap each child's build closure tore-insert the one injected cell at lowering (
node.rs:231-238), solower_items' disjoint-cursor walk (blueprint.rs:798-805) never learnsabout the gang and stays in mechanical agreement with
collect_params.param_space, one gang address enters —check_param_namespace_injective(
blueprint.rs:562) stays a pure name-uniqueness check (the structural curethe ledger already chose over
AmbiguousKnob, C9 cycle-0032).Feed'sRoleKindMismatchpredicate(
blueprint.rs:678-689); siblings within one composite for a first cut(cross-nested-composite breaks the per-frame cursor locality of
inline_composite).BLUEPRINT_FORMAT_VERSION1→2 bump — an old reader ignoring agang section would silently build N independent axes (a wrong graph), the
textbook Tier-2 case (
blueprint_serde.rs:152-163).bound_paramsisper-builder,
node.rs:157-164) or the graph render loses the gang relation.Divergent — the direction (bounced): which tier does a gang live at?
The minimal-mechanism stance surfaced a materially cheaper route and the others
countered it on cited grounds; the split is real. Three tiers, distinct
trade-offs:
every consumer sees; reproducible; consistent (C24/C25 say param-space is
blueprint-owned; C1/C18 say the coupling belongs in the hashed artifact). Cost:
a new persisted
Op+ a format-version bump + the engine mechanism +re-shipping the r_breakout_open / r_meanrev examples. Foundational and
hard to reverse. Fits "the pair is structurally one knob."
campaign document's axis vocabulary, zipping two blueprint param addresses for
an experiment. Reproducible (the campaign doc is a hashed, content-addressed
artifact — so it satisfies the C1/C18 concern) with NO blueprint/format change.
The blueprint still exposes two axes; the coupling is per-experiment. Moderate
cost (a campaign-axis construct + the sweep point-set zip). Fits "couple these
for this experiment."
--gang a,bsugar (invocation-only) — the lightest: a flag thatmakes the sweep enumerate a zipped diagonal instead of the cartesian grid,
over the existing
ListSpacerunner (exec.rs:515; the points today come fromthe cartesian
enumerate_grid/GridSpaceat:492, so this is newpoint-set logic + a flag, no engine-invariant/format change). Solves the
asymmetric-grid pain cheaply; least principled (the coupling is ephemeral /
re-asserted per run unless recorded).
The call the maintainer owns
Whether ganging is a structural property of the strategy (→ A, the swarm's
cited lean, foundational) or an intent of the experiment (→ B, reproducible
without a format bump), or just needs the asymmetric-grid pain gone cheaply (→ C),
is a value/semantics decision no source settles — and A commits a hard-to-reverse
BLUEPRINT_FORMAT_VERSIONbump on a "strikeable" idea. Orchestrator's lean: B— it captures the intent reproducibly, fits the campaign-as-experiment-intent
architecture, and avoids a format bump; escalate to A only if ganging should
be a permanent, reusable-across-experiments property of the strategy itself.
Direction ratified: blueprint construct — and the format-version call (2026-07-10)
Tier fork → blueprint construct (maintainer decision)
The maintainer ratified the blueprint-construct tier on 2026-07-10: parameter
ganging is a structural property of the strategy — a blueprint-level construction
op — not a campaign-document axis and not CLI sweep sugar. Recorded decision
intent: a gang is the author declaring that several child param slots are
semantically ONE knob; leaving them un-ganged makes the blueprint's declared
parameter space false — axis discovery lists N knobs where the strategy has one,
and a closed instance can bind them unequally, an instance the author never
designed. The deciding property is that the gang is binding, not advisory:
the child param addresses are removed from the parameter space and replaced by
one gang address (the existing bind closure-wrap pattern with the value deferred
instead of fixed), so no consumer can address the children independently — the
same "structurally impossible, not merely discouraged" pattern the causality
invariant uses.
Researcher-side experimental coupling of genuinely independent knobs (zipped /
diagonal sweeps over a still-un-ganged pair) is a distinct, unrequested feature
and stays out of scope; it would live at the experiment layer if ever needed.
Format-version fork → NO bump; the gang enters format v1 (derived, maintainer-prompted)
The design-discovery triage leaned Tier-2 (bump
BLUEPRINT_FORMAT_VERSION1→2)on the ground that an old reader silently ignoring a gangs section would build N
independent axes — a wrong graph. Re-examined on 2026-07-10 at the maintainer's
prompt against the project's actual deployment state, the decision is: no
bump — the gangs section is added as an additive optional field of format v1,
and
BLUEPRINT_FORMAT_VERSIONstays 1 until the first external ship freezes v1(gangs included). Rationale:
this repository (examples, test fixtures, the committed fieldtest evidence
corpus). Reader and writer are always built from the same commit and change
atomically — the reader/writer skew boundary the version gate protects does
not exist yet.
format_version != supportedrefuses, crates/aura-engine/src/blueprint_serde.rs:212), so a bump would
invalidate every existing v1 document at once: the committed historical
fieldtest corpus would become unloadable by the new binary (or need a mass
rewrite of tracked evidence artifacts), and all examples/fixtures would churn
— cost with no protected consumer on the other side.
gangs section defaults to "no gangs", the prior behaviour (the Tier-1 test).
The Tier-2 hazard (a reader that tolerates-but-ignores the section) can only
materialize against a stale pre-gang dev build fed a gang-bearing file —
dev-loop-only, self-inflicted, accepted.
is its activation point: the Tier-2 bump discipline binds from the first
external ship onward. The policy comment in the serde module and the design
ledger record this dormancy so a later pre-ship change does not bump by rote
— and so the first ship consciously freezes v1.
Status: design settled — mechanism (per the triage on this thread) plus tier and
version decisions above — ready for spec production.
Spec auto-signed on a grounding-check PASS (2026-07-10, no human in the loop)
The parameter-ganging spec (blueprint-construct tier, per the ratified
direction recorded earlier on this thread) was signed autonomously through the
grounding gate: an independent fresh-context check extracted the spec's
load-bearing assumptions about current behaviour — param-space emission
order, bind slot-removal mechanics, the serde unknown-optional-field tolerance
and canonical omit-defaults, the strict format-version equality refusal,
identity-id debug-name-blindness, the op-script/builder lockstep pin, and the
open examples' current two-axis namespaces — and ratified each against a
named, currently-green test. Verdict: PASS (ten assumptions ratified; one
non-blocking nuance recorded: op-script↔builder byte identity holds under
matched authoring, which the planned lockstep extension controls).
Design decisions the spec encodes beyond the mechanism triage on this thread:
param_space()projection + a compile-front point expansion. Thebind-closure-wrap fan-out from the triage was dropped — a build closure
sees only its own node's cursor slice, so a cross-node fan-out cannot live
inside it; the point expansion delivers the same semantics at the correct
seam, and the lowering cursor walk stays byte-identical for gang-free
blueprints.
op-script finish, serde load) via a single fallible constructor — the
established no-second-validator discipline; gang-name collisions surface
through the existing param-namespace injectivity check on the projected
space.
(node index, original param pos) coordinates plus a name string; the
identity projection blanks the names and keeps the structure — the exact
Role (name/targets) and bound-param (pos/name) precedents.
ganged single knobs (the breakout channel length; the mean-reversion
window, with the band factor staying an independent axis); the closed
examples already bind those pairs to one value by hand.
this thread; existing gang-free documents load and re-serialize
byte-identically.
Status: spec signed — plan production proceeding.
Shipped (2026-07-10) — cycle closed drift-clean
The gang construct is implemented end to end on the blueprint-construct tier
ratified on this thread, in five feature commits plus an audit-close (local,
unpushed; the closing commit references this issue for auto-close on push):
Gang/GangMember(Role name/targets split + BoundParampos/name dual),
Composite::with_gangsas the single fallible gate over theshared
check_gangspredicate;param_space()projects member addresses toONE public gang address at the first member's walk position; the compile
front expands the public point back onto the raw layout (gang cell
duplicated per member) — the lowering cursor walk and all build closures
untouched, byte-identical for gang-free blueprints.
{"op":"gang","as":…,"into":[…]}and theGraphBuilder::gangtwin, inC24 lockstep (byte-identical serialization pinned); eager by-identifier
refusals + holistic injectivity via the existing gate.
gangssection of format v1 — NO versionbump, per the dormancy decision on this thread; existing documents
round-trip byte-identically; identity ids stay debug-name-blind
(gang/member names blanked, structure survives).
--list-axes/introspect --params/ campaign axes /sweep/walkforward/mc/generalize all see one knob (they consume the
projected param space — zero per-verb changes); the graph render model
carries the gang relation and the viewer annotates ganged params.
r_breakout_opennow exposeschannel_length(the Donchianchannel is structurally one parameter),
r_meanrev_openexposeswindow+the independent
band.factor— matching what the closed builders alwaysbound by hand.
Cycle-close architect review: clean; its one [low] finding (the
projection/expansion mirror-walk untested for gang members interleaved with
other open params on the same node) was closed in the audit commit with a
dedicated equivalence pin. Full workspace suite 1104/0 (real-data e2e
included), lint and doc builds clean.