Parameter ganging: one knob driving several nodes (explicit composite-shared param) #61

Closed
opened 2026-06-13 18:32:37 +02:00 by Brummel · 5 comments
Owner

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): a
duplicated path is "a knob no binding can select alone" —
check_param_namespace_injective rejects it with DuplicateParamPath before any
binding resolves. Two nodes sharing a node_name() (or both empty) is a hard
error, 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 merging
identity.

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.

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): a duplicated path is "a knob no binding can select alone" — `check_param_namespace_injective` rejects it with `DuplicateParamPath` before any binding resolves. Two nodes sharing a `node_name()` (or both empty) is a hard error, 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 merging identity. 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.
Brummel added the idea label 2026-06-13 18:32:37 +02:00
Brummel added feature and removed idea labels 2026-06-21 15:06:45 +02:00
Brummel added this to the Triage harvest 2026-07 — surface honesty & authoring ergonomics milestone 2026-07-09 17:31:16 +02:00
Author
Owner

Triage 2026-07-09 (tree at 68317ec) — still unbuilt, and the cost of the gap grew with the blueprint-generic verbs:

  • No ganging construct exists: the construction Op vocabulary (crates/aura-engine/src/construction.rs:27-40 — Source/Input/Add/Feed/Connect/Expose) has no param-expose/gang op; grep for "gang" hits only forward-pointing comments (crates/aura-cli/src/main.rs:1825/:1861, crates/aura-cli/tests/graph_construct.rs:747).
  • The injectivity invariant this issue builds on is live: check_param_namespace_injective / DuplicateParamPath in aura-core node.rs and aura-engine construction.rs/blueprint.rs.
  • Relevance grew since #220: examples/r_breakout_open.json exposes channel_hi.length and channel_lo.length as two independent axes, so a repeatable --axis sweep 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).
  • Work shape: a composite-level construct exposing ONE injective param address fanning out 1:N into child slots (GraphBuilder surface + a construction Op + param_space fan-out), then r_breakout_open/r_meanrev re-shipped as single-ganged-axis examples.
Triage 2026-07-09 (tree at 68317ec) — still unbuilt, and the cost of the gap grew with the blueprint-generic verbs: - No ganging construct exists: the construction Op vocabulary (crates/aura-engine/src/construction.rs:27-40 — Source/Input/Add/Feed/Connect/Expose) has no param-expose/gang op; grep for "gang" hits only forward-pointing comments (crates/aura-cli/src/main.rs:1825/:1861, crates/aura-cli/tests/graph_construct.rs:747). - The injectivity invariant this issue builds on is live: check_param_namespace_injective / DuplicateParamPath in aura-core node.rs and aura-engine construction.rs/blueprint.rs. - Relevance grew since #220: examples/r_breakout_open.json exposes channel_hi.length and channel_lo.length as two independent axes, so a repeatable `--axis` sweep 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). - Work shape: a composite-level construct exposing ONE injective param address fanning out 1:N into child slots (GraphBuilder surface + a construction Op + param_space fan-out), then r_breakout_open/r_meanrev re-shipped as single-ganged-axis examples.
Author
Owner

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:

  • A new construction Op (a param gang, e.g. Gang { as_name, into: Vec<"node.slot"> }) mirroring Feed's role→N-slots fan-out — NOT overloading
    Expose (output-field-only, construction.rs:282). It must land on BOTH the
    op-script and Rust-builder cadences in lockstep (the "no second validator"
    C24 discipline, construction.rs:1-9).
  • Fan-out via the bind-style closure-wrap, not a side-table: remove the N
    child slots from param_space and wrap each child's build closure to
    re-insert the one injected cell at lowering (node.rs:231-238), so
    lower_items' disjoint-cursor walk (blueprint.rs:798-805) never learns
    about the gang and stays in mechanical agreement with collect_params.
  • Injectivity by removal, not gate-teaching: the N child addresses leave
    param_space, one gang address enters — check_param_namespace_injective
    (blueprint.rs:562) stays a pure name-uniqueness check (the structural cure
    the ledger already chose over AmbiguousKnob, C9 cycle-0032).
  • Kind-uniformity by reusing Feed's RoleKindMismatch predicate
    (blueprint.rs:678-689); siblings within one composite for a first cut
    (cross-nested-composite breaks the per-frame cursor locality of
    inline_composite).
  • A Tier-2 BLUEPRINT_FORMAT_VERSION 1→2 bump — an old reader ignoring a
    gang section would silently build N independent axes (a wrong graph), the
    textbook Tier-2 case (blueprint_serde.rs:152-163).
  • A cross-node render-symbol carrier is needed (today bound_params is
    per-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:

  • A. Blueprint construct (topology-owned) — the mechanism above. One axis
    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."
  • B. Campaign-document gang (experiment-intent-owned) — a gang lives in the
    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."
  • C. CLI --gang a,b sugar (invocation-only) — the lightest: a flag that
    makes the sweep enumerate a zipped diagonal instead of the cartesian grid,
    over the existing ListSpace runner (exec.rs:515; the points today come from
    the cartesian enumerate_grid/GridSpace at :492, so this is new
    point-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).
  • D. Defer — the original issue is "Strikeable; no commitment."

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_VERSION bump 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.

## 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: - **A new construction `Op`** (a param gang, e.g. `Gang { as_name, into: Vec<"node.slot"> }`) mirroring `Feed`'s role→N-slots fan-out — NOT overloading `Expose` (output-field-only, `construction.rs:282`). It must land on BOTH the op-script and Rust-builder cadences in lockstep (the "no second validator" C24 discipline, `construction.rs:1-9`). - **Fan-out via the `bind`-style closure-wrap**, not a side-table: remove the N child slots from `param_space` and wrap each child's build closure to re-insert the one injected cell at lowering (`node.rs:231-238`), so `lower_items`' disjoint-cursor walk (`blueprint.rs:798-805`) never learns about the gang and stays in mechanical agreement with `collect_params`. - **Injectivity by removal, not gate-teaching**: the N child addresses leave `param_space`, one gang address enters — `check_param_namespace_injective` (`blueprint.rs:562`) stays a pure name-uniqueness check (the structural cure the ledger already chose over `AmbiguousKnob`, C9 cycle-0032). - **Kind-uniformity by reusing `Feed`'s `RoleKindMismatch`** predicate (`blueprint.rs:678-689`); **siblings within one composite** for a first cut (cross-nested-composite breaks the per-frame cursor locality of `inline_composite`). - **A Tier-2 `BLUEPRINT_FORMAT_VERSION` 1→2 bump** — an old reader ignoring a gang section would silently build N independent axes (a wrong graph), the textbook Tier-2 case (`blueprint_serde.rs:152-163`). - A cross-node render-symbol carrier is needed (today `bound_params` is per-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: - **A. Blueprint construct** (topology-owned) — the mechanism above. One axis 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." - **B. Campaign-document gang** (experiment-intent-owned) — a gang lives in the 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*." - **C. CLI `--gang a,b` sugar** (invocation-only) — the lightest: a flag that makes the sweep enumerate a zipped diagonal instead of the cartesian grid, over the existing `ListSpace` runner (`exec.rs:515`; the points today come from the cartesian `enumerate_grid`/`GridSpace` at `:492`, so this is new point-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). - **D. Defer** — the original issue is "Strikeable; no commitment." ### 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_VERSION` bump 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.
Author
Owner

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_VERSION 1→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_VERSION stays 1 until the first external ship freezes v1
(gangs included). Rationale:

  • The project is unshipped and every existing blueprint document lives inside
    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.
  • The loader's version check is strict equality (format_version != supported
    refuses, 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.
  • Under no-bump, existing v1 documents remain valid byte-unchanged: an absent
    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.
  • The two-tier policy itself is unchanged and correct; what this decision adds
    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.

## 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_VERSION` 1→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_VERSION` stays 1 until the first external ship freezes v1 (gangs included). Rationale: - The project is unshipped and every existing blueprint document lives inside 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. - The loader's version check is strict equality (`format_version != supported` refuses, 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. - Under no-bump, existing v1 documents remain valid byte-unchanged: an absent 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. - The two-tier policy itself is unchanged and correct; what this decision adds 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.
Author
Owner

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:

  • Fan-out seam corrected: gang = composite-level metadata + a
    param_space() projection + a compile-front point expansion. The
    bind-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.
  • One validation predicate, three minting boundaries (builder build,
    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.
  • Identity stays debug-name-blind: gang members serialize as structural
    (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.
  • Examples converted to author intent: the two open examples expose their
    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.
  • No format-version bump, per the format-version decision recorded on
    this thread; existing gang-free documents load and re-serialize
    byte-identically.

Status: spec signed — plan production proceeding.

## 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: - **Fan-out seam corrected**: gang = composite-level metadata + a `param_space()` projection + a compile-front point expansion. The bind-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. - **One validation predicate, three minting boundaries** (builder build, 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. - **Identity stays debug-name-blind**: gang members serialize as structural (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. - **Examples converted to author intent**: the two open examples expose their 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. - **No format-version bump**, per the format-version decision recorded on this thread; existing gang-free documents load and re-serialize byte-identically. Status: spec signed — plan production proceeding.
Author
Owner

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):

  • Engine: Gang/GangMember (Role name/targets split + BoundParam
    pos/name dual), Composite::with_gangs as the single fallible gate over the
    shared check_gangs predicate; param_space() projects member addresses to
    ONE 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.
  • Authoring: the seventh construction op
    {"op":"gang","as":…,"into":[…]} and the GraphBuilder::gang twin, in
    C24 lockstep (byte-identical serialization pinned); eager by-identifier
    refusals + holistic injectivity via the existing gate.
  • Format: an additive-optional gangs section of format v1 — NO version
    bump, 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).
  • Surface: --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.
  • Examples: r_breakout_open now exposes channel_length (the Donchian
    channel is structurally one parameter), r_meanrev_open exposes window +
    the independent band.factor — matching what the closed builders always
    bound 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.

## 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): - **Engine**: `Gang`/`GangMember` (Role name/targets split + BoundParam pos/name dual), `Composite::with_gangs` as the single fallible gate over the shared `check_gangs` predicate; `param_space()` projects member addresses to ONE 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. - **Authoring**: the seventh construction op `{"op":"gang","as":…,"into":[…]}` and the `GraphBuilder::gang` twin, in C24 lockstep (byte-identical serialization pinned); eager by-identifier refusals + holistic injectivity via the existing gate. - **Format**: an additive-optional `gangs` section of format v1 — NO version bump, 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). - **Surface**: `--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. - **Examples**: `r_breakout_open` now exposes `channel_length` (the Donchian channel is structurally one parameter), `r_meanrev_open` exposes `window` + the independent `band.factor` — matching what the closed builders always bound 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.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Brummel/Aura#61