feat(cli,docs): ship the ganged open examples + document the gang construct (#61 tasks 7-8)
The two open examples now expose their author-intended single knobs — the closed builders always bound these pairs to one value by hand; the open forms falsely offered them as independent axes: - r_breakout_open: channel_hi.length + channel_lo.length -> channel_length (the Donchian channel is structurally ONE parameter) - r_meanrev_open: mean_window.length + var_window.length -> window; the band factor stays an independent axis Regenerated via the emitters (never hand-edited); the closed examples are byte-unchanged. The carve builders gang in the open branch only. Test migration: the two axis-namespace pins, five --real e2e invocations (single gang axis, the 10,20 diagonal for the campaign pair — the mismatched 20,40/10,20 grid was exactly the configuration space the gang retires), and the param_stability row counts (4 -> 3). The r-sma walkforward golden anchor is untouched (it never swept a ganged pair). Docs: authoring-guide gains the seventh op + the third param state (open/bound/ganged) + the gang wrap note; README op list + Axis concept; glossary gang entry; ledger C24 records the gang verb and the pre-ship Tier-2 dormancy (no format-version bump while no out-of-repo reader exists). Verified: full workspace suite 1104/0 (--real e2e included, local data present), clippy -D warnings clean, cargo doc clean; live acceptance: introspect --params prints channel_length:I64 alone / window:I64 + band.factor:F64. closes #61
This commit is contained in:
@@ -39,7 +39,8 @@ Invoke it as `aura <command> …` (examples below use the plain name).
|
|||||||
a Monte-Carlo seed set, a walk-forward window sequence). Families are persisted
|
a Monte-Carlo seed set, a walk-forward window sequence). Families are persisted
|
||||||
in a content-addressed store and can be listed, ranked, and reproduced.
|
in a content-addressed store and can be listed, ranked, and reproduced.
|
||||||
- **Axis** — one named, sweepable knob of a blueprint (e.g. `graph.fast.length`),
|
- **Axis** — one named, sweepable knob of a blueprint (e.g. `graph.fast.length`),
|
||||||
bound with a comma-separated value list.
|
bound with a comma-separated value list. A **gang** fuses several sibling
|
||||||
|
knobs into one axis (one value drives all members).
|
||||||
|
|
||||||
## Running & orchestrating a loaded blueprint
|
## Running & orchestrating a loaded blueprint
|
||||||
|
|
||||||
@@ -115,7 +116,8 @@ aura graph build < crossover.ops.json > crossover.bp.json
|
|||||||
aura sweep crossover.bp.json --list-axes
|
aura sweep crossover.bp.json --list-axes
|
||||||
```
|
```
|
||||||
|
|
||||||
The op kinds are `source`, `input`, `add`, `feed`, `connect`, and `expose`. See
|
The op kinds are `source`, `input`, `add`, `feed`, `connect`, `expose`, and
|
||||||
|
`gang`. See
|
||||||
`aura graph introspect --node <T>` for a type's exact ports and the op-script
|
`aura graph introspect --node <T>` for a type's exact ports and the op-script
|
||||||
grammar in the design ledger for the full semantics.
|
grammar in the design ledger for the full semantics.
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"format_version":1,"blueprint":{"name":"r_breakout_signal","nodes":[{"primitive":{"type":"Delay","bound":[{"pos":0,"name":"lag","kind":"I64","value":{"I64":1}}]}},{"primitive":{"type":"RollingMax","name":"channel_hi"}},{"primitive":{"type":"RollingMin","name":"channel_lo"}},{"primitive":{"type":"Gt"}},{"primitive":{"type":"Gt"}},{"primitive":{"type":"Latch"}},{"primitive":{"type":"Latch"}},{"primitive":{"type":"Sub"}}],"edges":[{"from":0,"to":1,"slot":0,"from_field":0},{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":3,"slot":1,"from_field":0},{"from":2,"to":4,"slot":0,"from_field":0},{"from":3,"to":5,"slot":0,"from_field":0},{"from":4,"to":5,"slot":1,"from_field":0},{"from":4,"to":6,"slot":0,"from_field":0},{"from":3,"to":6,"slot":1,"from_field":0},{"from":5,"to":7,"slot":0,"from_field":0},{"from":6,"to":7,"slot":1,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":3,"slot":0},{"node":4,"slot":1}],"source":"F64"}],"output":[{"node":7,"field":0,"name":"bias"}]}}
|
{"format_version":1,"blueprint":{"name":"r_breakout_signal","nodes":[{"primitive":{"type":"Delay","bound":[{"pos":0,"name":"lag","kind":"I64","value":{"I64":1}}]}},{"primitive":{"type":"RollingMax","name":"channel_hi"}},{"primitive":{"type":"RollingMin","name":"channel_lo"}},{"primitive":{"type":"Gt"}},{"primitive":{"type":"Gt"}},{"primitive":{"type":"Latch"}},{"primitive":{"type":"Latch"}},{"primitive":{"type":"Sub"}}],"edges":[{"from":0,"to":1,"slot":0,"from_field":0},{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":3,"slot":1,"from_field":0},{"from":2,"to":4,"slot":0,"from_field":0},{"from":3,"to":5,"slot":0,"from_field":0},{"from":4,"to":5,"slot":1,"from_field":0},{"from":4,"to":6,"slot":0,"from_field":0},{"from":3,"to":6,"slot":1,"from_field":0},{"from":5,"to":7,"slot":0,"from_field":0},{"from":6,"to":7,"slot":1,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":3,"slot":0},{"node":4,"slot":1}],"source":"F64"}],"output":[{"node":7,"field":0,"name":"bias"}],"gangs":[{"name":"channel_length","kind":"I64","members":[{"node":1,"pos":0,"name":"length"},{"node":2,"pos":0,"name":"length"}]}]}}
|
||||||
@@ -1 +1 @@
|
|||||||
{"format_version":1,"blueprint":{"name":"r_meanrev_signal","nodes":[{"primitive":{"type":"EMA","name":"mean_window"}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Mul"}},{"primitive":{"type":"EMA","name":"var_window"}},{"primitive":{"type":"Sqrt"}},{"primitive":{"type":"Scale","name":"band"}},{"primitive":{"type":"Add"}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Gt"}},{"primitive":{"type":"Gt"}},{"primitive":{"type":"Latch"}},{"primitive":{"type":"Latch"}},{"primitive":{"type":"Sub"}}],"edges":[{"from":0,"to":1,"slot":1,"from_field":0},{"from":1,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0},{"from":3,"to":4,"slot":0,"from_field":0},{"from":4,"to":5,"slot":0,"from_field":0},{"from":0,"to":6,"slot":0,"from_field":0},{"from":5,"to":6,"slot":1,"from_field":0},{"from":0,"to":7,"slot":0,"from_field":0},{"from":5,"to":7,"slot":1,"from_field":0},{"from":6,"to":8,"slot":1,"from_field":0},{"from":7,"to":9,"slot":0,"from_field":0},{"from":8,"to":10,"slot":0,"from_field":0},{"from":9,"to":10,"slot":1,"from_field":0},{"from":9,"to":11,"slot":0,"from_field":0},{"from":8,"to":11,"slot":1,"from_field":0},{"from":11,"to":12,"slot":0,"from_field":0},{"from":10,"to":12,"slot":1,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0},{"node":8,"slot":0},{"node":9,"slot":1}],"source":"F64"}],"output":[{"node":12,"field":0,"name":"bias"}]}}
|
{"format_version":1,"blueprint":{"name":"r_meanrev_signal","nodes":[{"primitive":{"type":"EMA","name":"mean_window"}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Mul"}},{"primitive":{"type":"EMA","name":"var_window"}},{"primitive":{"type":"Sqrt"}},{"primitive":{"type":"Scale","name":"band"}},{"primitive":{"type":"Add"}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Gt"}},{"primitive":{"type":"Gt"}},{"primitive":{"type":"Latch"}},{"primitive":{"type":"Latch"}},{"primitive":{"type":"Sub"}}],"edges":[{"from":0,"to":1,"slot":1,"from_field":0},{"from":1,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0},{"from":3,"to":4,"slot":0,"from_field":0},{"from":4,"to":5,"slot":0,"from_field":0},{"from":0,"to":6,"slot":0,"from_field":0},{"from":5,"to":6,"slot":1,"from_field":0},{"from":0,"to":7,"slot":0,"from_field":0},{"from":5,"to":7,"slot":1,"from_field":0},{"from":6,"to":8,"slot":1,"from_field":0},{"from":7,"to":9,"slot":0,"from_field":0},{"from":8,"to":10,"slot":0,"from_field":0},{"from":9,"to":10,"slot":1,"from_field":0},{"from":9,"to":11,"slot":0,"from_field":0},{"from":8,"to":11,"slot":1,"from_field":0},{"from":11,"to":12,"slot":0,"from_field":0},{"from":10,"to":12,"slot":1,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0},{"node":8,"slot":0},{"node":9,"slot":1}],"source":"F64"}],"output":[{"node":12,"field":0,"name":"bias"}],"gangs":[{"name":"window","kind":"I64","members":[{"node":0,"pos":0,"name":"length"},{"node":3,"pos":0,"name":"length"}]}]}}
|
||||||
@@ -1831,8 +1831,8 @@ const R_BREAKOUT_CHANNEL: i64 = 3;
|
|||||||
/// as blueprint data (#159 cut 2). The signal computation matches the retired fused
|
/// as blueprint data (#159 cut 2). The signal computation matches the retired fused
|
||||||
/// builder's leg (same nodes, same wiring); the pip/R harness is the generic
|
/// builder's leg (same nodes, same wiring); the pip/R harness is the generic
|
||||||
/// `wrap_r` wrapper, not part of the signal. `channel = Some(n)` binds both rolling
|
/// `wrap_r` wrapper, not part of the signal. `channel = Some(n)` binds both rolling
|
||||||
/// nodes (closed); `None` leaves them open (two sweep axes — per-node exposure;
|
/// nodes (closed); `None` leaves them open, ganged into the single `channel_length`
|
||||||
/// ganging one knob into both is the separate #61). This carve has no production
|
/// knob (#61) — the channel is structurally ONE parameter. This carve has no production
|
||||||
/// (non-test) caller — it is exercised only by the regeneration + proof tests below;
|
/// (non-test) caller — it is exercised only by the regeneration + proof tests below;
|
||||||
/// production code loads the shipped JSON, not this builder — hence `#[cfg(test)]`.
|
/// production code loads the shipped JSON, not this builder — hence `#[cfg(test)]`.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -1847,6 +1847,9 @@ fn r_breakout_signal(channel: Option<i64>) -> Composite {
|
|||||||
}
|
}
|
||||||
let mx = g.add(mx_b);
|
let mx = g.add(mx_b);
|
||||||
let mn = g.add(mn_b);
|
let mn = g.add(mn_b);
|
||||||
|
if channel.is_none() {
|
||||||
|
g.gang("channel_length", [mx.param("length"), mn.param("length")]);
|
||||||
|
}
|
||||||
let gt_up = g.add(Gt::builder());
|
let gt_up = g.add(Gt::builder());
|
||||||
let gt_down = g.add(Gt::builder());
|
let gt_down = g.add(Gt::builder());
|
||||||
let up_latch = g.add(Latch::builder());
|
let up_latch = g.add(Latch::builder());
|
||||||
@@ -1869,8 +1872,9 @@ fn r_breakout_signal(channel: Option<i64>) -> Composite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The EWMA window for the canonical closed r_meanrev example (ganged mean/var Ema
|
/// The EWMA window for the canonical closed r_meanrev example (ganged mean/var Ema
|
||||||
/// length). Single source for the emitter and the proof tests that must all agree
|
/// length; the open form gangs them structurally via the single `window` knob, #61).
|
||||||
/// with the baked `examples/r_meanrev.json`.
|
/// Single source for the emitter and the proof tests that must all agree with the
|
||||||
|
/// baked `examples/r_meanrev.json`.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
const R_MEANREV_WINDOW: i64 = 3;
|
const R_MEANREV_WINDOW: i64 = 3;
|
||||||
|
|
||||||
@@ -1898,6 +1902,9 @@ fn r_meanrev_signal(window: Option<i64>, band_k: Option<f64>) -> Composite {
|
|||||||
let dev = g.add(Sub::builder()); // price - mean
|
let dev = g.add(Sub::builder()); // price - mean
|
||||||
let sq = g.add(Mul::builder()); // dev * dev
|
let sq = g.add(Mul::builder()); // dev * dev
|
||||||
let var = g.add(var_b); // EWMA variance
|
let var = g.add(var_b); // EWMA variance
|
||||||
|
if window.is_none() {
|
||||||
|
g.gang("window", [mean.param("length"), var.param("length")]);
|
||||||
|
}
|
||||||
let sigma = g.add(Sqrt::builder());
|
let sigma = g.add(Sqrt::builder());
|
||||||
let mut band_b = Scale::builder().named("band");
|
let mut band_b = Scale::builder().named("band");
|
||||||
if let Some(k) = band_k {
|
if let Some(k) = band_k {
|
||||||
|
|||||||
@@ -1739,7 +1739,7 @@ fn generalize_real_e2e_pins_the_exact_current_grade() {
|
|||||||
/// `r_sma_open.json`, so a regression that silently special-cased r-sma paths
|
/// `r_sma_open.json`, so a regression that silently special-cased r-sma paths
|
||||||
/// back in (e.g. resolving axes against a hardcoded `sma_signal.*` namespace)
|
/// back in (e.g. resolving axes against a hardcoded `sma_signal.*` namespace)
|
||||||
/// would ship green there while breaking every other blueprint. This runs the
|
/// would ship green there while breaking every other blueprint. This runs the
|
||||||
/// unrelated r-breakout candidate (`channel_hi`/`channel_lo` axes, no
|
/// unrelated r-breakout candidate (the ganged `channel_length` axis, no
|
||||||
/// `sma_signal` node in sight) end-to-end and asserts a well-formed two-
|
/// `sma_signal` node in sight) end-to-end and asserts a well-formed two-
|
||||||
/// instrument grade. Gated on the shared GER40/USDJPY Sept-2024 archive; skips
|
/// instrument grade. Gated on the shared GER40/USDJPY Sept-2024 archive; skips
|
||||||
/// cleanly on a data refusal.
|
/// cleanly on a data refusal.
|
||||||
@@ -1752,8 +1752,7 @@ fn generalize_grades_a_non_r_sma_blueprint() {
|
|||||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||||
.args([
|
.args([
|
||||||
"generalize", &fixture, "--real", "GER40,USDJPY",
|
"generalize", &fixture, "--real", "GER40,USDJPY",
|
||||||
"--axis", "r_breakout_signal.channel_hi.length=20",
|
"--axis", "r_breakout_signal.channel_length=20",
|
||||||
"--axis", "r_breakout_signal.channel_lo.length=20",
|
|
||||||
"--stop-length", "14", "--stop-k", "2.0",
|
"--stop-length", "14", "--stop-k", "2.0",
|
||||||
"--from", FROM_MS, "--to", TO_MS,
|
"--from", FROM_MS, "--to", TO_MS,
|
||||||
])
|
])
|
||||||
@@ -1848,9 +1847,9 @@ fn walkforward_real_e2e_pins_the_exact_current_grade() {
|
|||||||
/// namespace (e.g. in `walkforward_summary_json_from_reports`'s per-axis
|
/// namespace (e.g. in `walkforward_summary_json_from_reports`'s per-axis
|
||||||
/// `raw_matches_wrapped` search) would ship green there while breaking every
|
/// `raw_matches_wrapped` search) would ship green there while breaking every
|
||||||
/// other blueprint. This runs the unrelated r-breakout candidate
|
/// other blueprint. This runs the unrelated r-breakout candidate
|
||||||
/// (`channel_hi`/`channel_lo` axes, no `sma_signal` node in sight) end-to-end
|
/// (the ganged `channel_length` axis, no `sma_signal` node in sight) end-to-end
|
||||||
/// and asserts a well-formed multi-window summary whose `param_stability` has
|
/// and asserts a well-formed multi-window summary whose `param_stability` has
|
||||||
/// exactly one entry per bound axis (2 blueprint axes + the 2 stop columns).
|
/// exactly one entry per bound axis (1 ganged blueprint axis + the 2 stop columns).
|
||||||
/// The window count (9) is roller/data-derived, not strategy-derived, so it is
|
/// The window count (9) is roller/data-derived, not strategy-derived, so it is
|
||||||
/// pinned exactly against the same GER40 2025 span the r-sma anchor uses.
|
/// pinned exactly against the same GER40 2025 span the r-sma anchor uses.
|
||||||
/// Gated on the shared GER40 archive; skips cleanly on a data refusal.
|
/// Gated on the shared GER40 archive; skips cleanly on a data refusal.
|
||||||
@@ -1863,8 +1862,7 @@ fn walkforward_dissolves_a_non_r_sma_blueprint() {
|
|||||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||||
.args([
|
.args([
|
||||||
"walkforward", &fixture, "--real", "GER40",
|
"walkforward", &fixture, "--real", "GER40",
|
||||||
"--axis", "r_breakout_signal.channel_hi.length=10,20",
|
"--axis", "r_breakout_signal.channel_length=10,20",
|
||||||
"--axis", "r_breakout_signal.channel_lo.length=10,20",
|
|
||||||
"--stop-length", "14", "--stop-k", "2.0",
|
"--stop-length", "14", "--stop-k", "2.0",
|
||||||
"--from", FROM_MS, "--to", TO_MS,
|
"--from", FROM_MS, "--to", TO_MS,
|
||||||
])
|
])
|
||||||
@@ -1891,7 +1889,7 @@ fn walkforward_dissolves_a_non_r_sma_blueprint() {
|
|||||||
assert_eq!(wf["windows"].as_u64(), Some(9), "window count (data/roller-derived): {grade_line}");
|
assert_eq!(wf["windows"].as_u64(), Some(9), "window count (data/roller-derived): {grade_line}");
|
||||||
assert!(wf["oos_r"]["n_trades"].as_u64().is_some(), "pooled OOS trade count present: {grade_line}");
|
assert!(wf["oos_r"]["n_trades"].as_u64().is_some(), "pooled OOS trade count present: {grade_line}");
|
||||||
let ps = wf["param_stability"].as_array().expect("param_stability is an array");
|
let ps = wf["param_stability"].as_array().expect("param_stability is an array");
|
||||||
assert_eq!(ps.len(), 4, "one entry per bound axis (channel_hi, channel_lo, stop_length, stop_k): {grade_line}");
|
assert_eq!(ps.len(), 3, "one entry per bound axis (channel_length, stop_length, stop_k): {grade_line}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fork A: the dissolved walkforward binds the stop as a single risk regime, so a
|
/// Fork A: the dissolved walkforward binds the stop as a single risk regime, so a
|
||||||
@@ -3882,7 +3880,7 @@ fn mc_r_bootstrap_real_e2e_pins_the_exact_current_grade() {
|
|||||||
/// exercises `r_sma_open.json`; a regression that silently special-cased the
|
/// exercises `r_sma_open.json`; a regression that silently special-cased the
|
||||||
/// pipeline's axis lookup back to a hardcoded `sma_signal.*` namespace would
|
/// pipeline's axis lookup back to a hardcoded `sma_signal.*` namespace would
|
||||||
/// ship green there while breaking every other blueprint. This runs the
|
/// ship green there while breaking every other blueprint. This runs the
|
||||||
/// unrelated r-breakout candidate (`channel_hi`/`channel_lo` axes, no
|
/// unrelated r-breakout candidate (the ganged `channel_length` axis, no
|
||||||
/// `sma_signal` node in sight) end-to-end and asserts a well-formed
|
/// `sma_signal` node in sight) end-to-end and asserts a well-formed
|
||||||
/// `mc_r_bootstrap` grade: the argv-echoed knobs (`block_len`/`n_resamples`)
|
/// `mc_r_bootstrap` grade: the argv-echoed knobs (`block_len`/`n_resamples`)
|
||||||
/// pin exactly, `n_trades`/`e_r.mean`/`prob_le_zero` are asserted present and
|
/// pin exactly, `n_trades`/`e_r.mean`/`prob_le_zero` are asserted present and
|
||||||
@@ -3898,8 +3896,7 @@ fn mc_dissolves_a_non_r_sma_blueprint() {
|
|||||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||||
.args([
|
.args([
|
||||||
"mc", &fixture, "--real", "GER40",
|
"mc", &fixture, "--real", "GER40",
|
||||||
"--axis", "r_breakout_signal.channel_hi.length=10,20",
|
"--axis", "r_breakout_signal.channel_length=10,20",
|
||||||
"--axis", "r_breakout_signal.channel_lo.length=10,20",
|
|
||||||
"--stop-length", "14", "--stop-k", "2.0",
|
"--stop-length", "14", "--stop-k", "2.0",
|
||||||
"--block-len", "5", "--resamples", "1000", "--seed", "42",
|
"--block-len", "5", "--resamples", "1000", "--seed", "42",
|
||||||
"--from", FROM_MS, "--to", TO_MS,
|
"--from", FROM_MS, "--to", TO_MS,
|
||||||
@@ -4175,8 +4172,7 @@ fn walkforward_campaign_runs_an_arbitrary_blueprint() {
|
|||||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||||
.args([
|
.args([
|
||||||
"walkforward", &fixture, "--real", "GER40",
|
"walkforward", &fixture, "--real", "GER40",
|
||||||
"--axis", "r_breakout_signal.channel_hi.length=20,40",
|
"--axis", "r_breakout_signal.channel_length=10,20",
|
||||||
"--axis", "r_breakout_signal.channel_lo.length=10,20",
|
|
||||||
"--stop-length", "3", "--stop-k", "2.0",
|
"--stop-length", "3", "--stop-k", "2.0",
|
||||||
"--from", FROM_MS, "--to", TO_MS,
|
"--from", FROM_MS, "--to", TO_MS,
|
||||||
])
|
])
|
||||||
@@ -4207,9 +4203,9 @@ fn walkforward_campaign_runs_an_arbitrary_blueprint() {
|
|||||||
let v: serde_json::Value = serde_json::from_str(grade_line).expect("summary line parses as JSON");
|
let v: serde_json::Value = serde_json::from_str(grade_line).expect("summary line parses as JSON");
|
||||||
let wf = &v["walkforward"];
|
let wf = &v["walkforward"];
|
||||||
assert!(wf["windows"].as_u64().is_some_and(|w| w >= 1), "at least one window: {grade_line}");
|
assert!(wf["windows"].as_u64().is_some_and(|w| w >= 1), "at least one window: {grade_line}");
|
||||||
// param_stability rows: channel_hi, channel_lo, stop_length, stop_k.
|
// param_stability rows: channel_length, stop_length, stop_k.
|
||||||
let ps = wf["param_stability"].as_array().expect("param_stability is an array");
|
let ps = wf["param_stability"].as_array().expect("param_stability is an array");
|
||||||
assert_eq!(ps.len(), 4, "two channel axes + the stop pair: {grade_line}");
|
assert_eq!(ps.len(), 3, "one ganged channel axis + the stop pair: {grade_line}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// #220 weld-gone proof for mc: the R-bootstrap pipeline runs the same
|
/// #220 weld-gone proof for mc: the R-bootstrap pipeline runs the same
|
||||||
@@ -4227,8 +4223,7 @@ fn mc_campaign_runs_an_arbitrary_blueprint() {
|
|||||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||||
.args([
|
.args([
|
||||||
"mc", &fixture, "--real", "GER40",
|
"mc", &fixture, "--real", "GER40",
|
||||||
"--axis", "r_breakout_signal.channel_hi.length=20,40",
|
"--axis", "r_breakout_signal.channel_length=10,20",
|
||||||
"--axis", "r_breakout_signal.channel_lo.length=10,20",
|
|
||||||
"--stop-length", "3", "--stop-k", "2.0",
|
"--stop-length", "3", "--stop-k", "2.0",
|
||||||
"--block-len", "5", "--resamples", "100", "--seed", "7",
|
"--block-len", "5", "--resamples", "100", "--seed", "7",
|
||||||
"--from", FROM_MS, "--to", TO_MS,
|
"--from", FROM_MS, "--to", TO_MS,
|
||||||
@@ -4263,8 +4258,9 @@ fn mc_campaign_runs_an_arbitrary_blueprint() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// #220 weld-gone proof for generalize: one mean-reversion candidate graded
|
/// #220 weld-gone proof for generalize: one mean-reversion candidate graded
|
||||||
/// across two instruments — three axes with names the CLI has never heard of,
|
/// across two instruments — two axes with names the CLI has never heard of —
|
||||||
/// including an F64 axis. Gated on the GER40/USDJPY Sept-2024 archive.
|
/// one ganged I64 window and an independent F64 band. Gated on the GER40/USDJPY
|
||||||
|
/// Sept-2024 archive.
|
||||||
#[test]
|
#[test]
|
||||||
fn generalize_campaign_runs_an_arbitrary_blueprint() {
|
fn generalize_campaign_runs_an_arbitrary_blueprint() {
|
||||||
if !local_data_present() {
|
if !local_data_present() {
|
||||||
@@ -4276,8 +4272,7 @@ fn generalize_campaign_runs_an_arbitrary_blueprint() {
|
|||||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||||
.args([
|
.args([
|
||||||
"generalize", &fixture, "--real", "GER40,USDJPY",
|
"generalize", &fixture, "--real", "GER40,USDJPY",
|
||||||
"--axis", "r_meanrev_signal.mean_window.length=20",
|
"--axis", "r_meanrev_signal.window=20",
|
||||||
"--axis", "r_meanrev_signal.var_window.length=20",
|
|
||||||
"--axis", "r_meanrev_signal.band.factor=2.0",
|
"--axis", "r_meanrev_signal.band.factor=2.0",
|
||||||
"--stop-length", "3", "--stop-k", "2.0",
|
"--stop-length", "3", "--stop-k", "2.0",
|
||||||
"--from", GER40_SEPT2024_FROM_MS, "--to", GER40_SEPT2024_TO_MS,
|
"--from", GER40_SEPT2024_FROM_MS, "--to", GER40_SEPT2024_TO_MS,
|
||||||
|
|||||||
@@ -744,7 +744,8 @@ fn shipped_r_breakout_example_is_genuinely_closed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Property (#159 cut 2): the shipped open example (`examples/r_breakout_open.json`)
|
/// Property (#159 cut 2): the shipped open example (`examples/r_breakout_open.json`)
|
||||||
/// lists its two channel axes, in lowering order (per-node exposure; ganging is #61).
|
/// lists its ONE ganged channel axis (#61: the two rolling windows are structurally
|
||||||
|
/// one knob).
|
||||||
#[test]
|
#[test]
|
||||||
fn shipped_r_breakout_open_example_lists_its_axis_namespace() {
|
fn shipped_r_breakout_open_example_lists_its_axis_namespace() {
|
||||||
let dir = temp_cwd("r-breakout-example-open-params");
|
let dir = temp_cwd("r-breakout-example-open-params");
|
||||||
@@ -752,8 +753,47 @@ fn shipped_r_breakout_open_example_lists_its_axis_namespace() {
|
|||||||
run_in(&dir, &["graph", "introspect", "--params", &example("r_breakout_open.json")]);
|
run_in(&dir, &["graph", "introspect", "--params", &example("r_breakout_open.json")]);
|
||||||
assert_eq!(code, Some(0), "stdout: {stdout} stderr: {stderr}");
|
assert_eq!(code, Some(0), "stdout: {stdout} stderr: {stderr}");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stdout, "channel_hi.length:I64\nchannel_lo.length:I64\n",
|
stdout, "channel_length:I64\n",
|
||||||
"the raw open params, in order, from the public gallery copy"
|
"the ganged channel knob — one public axis for the two rolling windows (#61)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Property (#61 + #159 cut 2): binding the shipped open example's ganged
|
||||||
|
/// `channel_length` axis through the REAL `aura sweep` CLI pipeline (argv ->
|
||||||
|
/// `parse_axes` -> `compile_with_cells`'s gang expansion) reproduces the exact
|
||||||
|
/// metrics of running the shipped CLOSED example, whose `channel_hi`/
|
||||||
|
/// `channel_lo` are separately hardwired to the same value. The equivalence
|
||||||
|
/// between a gang and its member-bound twin is already pinned at the builder/
|
||||||
|
/// engine level (aura-engine's `gang_e2e.rs`, and this crate's
|
||||||
|
/// `r_breakout_example_loaded_runs_identically_to_the_carved_signal`, which
|
||||||
|
/// calls `run_signal_r` directly); this instead drives the actual public
|
||||||
|
/// binary end-to-end on the actual shipped files, so a regression in the
|
||||||
|
/// CLI's own axis-name resolution or grid-point binding (e.g. silently
|
||||||
|
/// treating the gang as an unbound param, or fanning the value to only one
|
||||||
|
/// member) would be caught here even if the internal builder-level
|
||||||
|
/// equivalence still holds.
|
||||||
|
#[test]
|
||||||
|
fn shipped_r_breakout_open_gang_axis_matches_the_closed_example() {
|
||||||
|
let closed_dir = temp_cwd("r-breakout-gang-axis-closed");
|
||||||
|
let (closed_stdout, closed_stderr, closed_code) = run_in(&closed_dir, &["run", &example("r_breakout.json")]);
|
||||||
|
assert_eq!(closed_code, Some(0), "closed run stderr: {closed_stderr}");
|
||||||
|
let closed: serde_json::Value =
|
||||||
|
serde_json::from_str(closed_stdout.trim()).expect("closed run report parses as JSON");
|
||||||
|
|
||||||
|
let sweep_dir = temp_cwd("r-breakout-gang-axis-sweep");
|
||||||
|
let (sweep_stdout, sweep_stderr, sweep_code) = run_in(
|
||||||
|
&sweep_dir,
|
||||||
|
&["sweep", &example("r_breakout_open.json"), "--axis", "r_breakout_signal.channel_length=3"],
|
||||||
|
);
|
||||||
|
assert_eq!(sweep_code, Some(0), "sweep stderr: {sweep_stderr}");
|
||||||
|
let lines: Vec<&str> = sweep_stdout.lines().collect();
|
||||||
|
assert_eq!(lines.len(), 1, "one grid point for a single-value axis: {sweep_stdout}");
|
||||||
|
let member: serde_json::Value = serde_json::from_str(lines[0]).expect("member line parses as JSON");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
member["report"]["metrics"], closed["metrics"],
|
||||||
|
"channel_length=3, bound via the real sweep CLI, must fan out to both channel_hi \
|
||||||
|
and channel_lo identically to the closed example's hardwired channel=3"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -777,8 +817,45 @@ fn shipped_r_meanrev_open_example_lists_its_axis_namespace() {
|
|||||||
run_in(&dir, &["graph", "introspect", "--params", &example("r_meanrev_open.json")]);
|
run_in(&dir, &["graph", "introspect", "--params", &example("r_meanrev_open.json")]);
|
||||||
assert_eq!(code, Some(0), "stdout: {stdout} stderr: {stderr}");
|
assert_eq!(code, Some(0), "stdout: {stdout} stderr: {stderr}");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stdout, "mean_window.length:I64\nvar_window.length:I64\nband.factor:F64\n",
|
stdout, "window:I64\nband.factor:F64\n",
|
||||||
"the raw open params, in lowering order"
|
"the ganged EWMA window + the independent band knob, in lowering order"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Property (#61 + #159 cut 3): a sweep over TWO axes at once — the ganged
|
||||||
|
/// `window` knob (fusing `mean_window.length`/`var_window.length`) alongside
|
||||||
|
/// the independent, un-ganged `band.factor` knob — binds both correctly
|
||||||
|
/// through the real CLI grid, matching the shipped closed example's hardwired
|
||||||
|
/// window=3/band=2.0. This is a distinct risk from the r_breakout gang test
|
||||||
|
/// (a lone gang axis): a mis-scoped gang-expansion map that shifts sibling
|
||||||
|
/// param positions could silently mis-bind the co-present un-ganged axis
|
||||||
|
/// instead (or vice-versa) even though each axis alone still resolves.
|
||||||
|
#[test]
|
||||||
|
fn shipped_r_meanrev_open_gang_plus_plain_axis_matches_the_closed_example() {
|
||||||
|
let closed_dir = temp_cwd("r-meanrev-gang-axis-closed");
|
||||||
|
let (closed_stdout, closed_stderr, closed_code) = run_in(&closed_dir, &["run", &example("r_meanrev.json")]);
|
||||||
|
assert_eq!(closed_code, Some(0), "closed run stderr: {closed_stderr}");
|
||||||
|
let closed: serde_json::Value =
|
||||||
|
serde_json::from_str(closed_stdout.trim()).expect("closed run report parses as JSON");
|
||||||
|
|
||||||
|
let sweep_dir = temp_cwd("r-meanrev-gang-axis-sweep");
|
||||||
|
let (sweep_stdout, sweep_stderr, sweep_code) = run_in(
|
||||||
|
&sweep_dir,
|
||||||
|
&[
|
||||||
|
"sweep", &example("r_meanrev_open.json"),
|
||||||
|
"--axis", "r_meanrev_signal.window=3",
|
||||||
|
"--axis", "r_meanrev_signal.band.factor=2.0",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
assert_eq!(sweep_code, Some(0), "sweep stderr: {sweep_stderr}");
|
||||||
|
let lines: Vec<&str> = sweep_stdout.lines().collect();
|
||||||
|
assert_eq!(lines.len(), 1, "one grid point for two single-value axes: {sweep_stdout}");
|
||||||
|
let member: serde_json::Value = serde_json::from_str(lines[0]).expect("member line parses as JSON");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
member["report"]["metrics"], closed["metrics"],
|
||||||
|
"window=3 (ganged) + band.factor=2.0 (plain), bound via the real sweep CLI, must \
|
||||||
|
match the closed example's hardwired window=3/band=2.0"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-1
@@ -31,7 +31,7 @@ $ aura graph build < smacross.json > blueprint.json
|
|||||||
Nodes are referenced by an **identifier** (given by `add`, see below); ports
|
Nodes are referenced by an **identifier** (given by `add`, see below); ports
|
||||||
are dotted `<identifier>.<port>` on both sides of a wire.
|
are dotted `<identifier>.<port>` on both sides of a wire.
|
||||||
|
|
||||||
### The six ops
|
### The seven ops
|
||||||
|
|
||||||
| op | JSON shape | does |
|
| op | JSON shape | does |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -41,6 +41,7 @@ are dotted `<identifier>.<port>` on both sides of a wire.
|
|||||||
| `feed` | `{"op":"feed","role":<str>,"into":[<port>, …]}` | fan a previously-declared role into one or more interior input slots, all-or-nothing (a failing target leaves none of the batch wired). |
|
| `feed` | `{"op":"feed","role":<str>,"into":[<port>, …]}` | fan a previously-declared role into one or more interior input slots, all-or-nothing (a failing target leaves none of the batch wired). |
|
||||||
| `connect` | `{"op":"connect","from":<port>,"to":<port>}` | wire one interior output field to one interior input slot. A `connect` that would close a dataflow cycle is rejected immediately — the only legal feedback path is an explicit delay/state node (domain invariant 5). |
|
| `connect` | `{"op":"connect","from":<port>,"to":<port>}` | wire one interior output field to one interior input slot. A `connect` that would close a dataflow cycle is rejected immediately — the only legal feedback path is an explicit delay/state node (domain invariant 5). |
|
||||||
| `expose` | `{"op":"expose","from":<port>,"as":<str>}` | promote an interior output field to a boundary output under the alias `as` — the only op whose name key is a real *alias* (contrast `add`'s `name`, which is an identifier, not a rename). |
|
| `expose` | `{"op":"expose","from":<port>,"as":<str>}` | promote an interior output field to a boundary output under the alias `as` — the only op whose name key is a real *alias* (contrast `add`'s `name`, which is an identifier, not a rename). |
|
||||||
|
| `gang` | `{"op":"gang","as":"channel_length","into":["channel_hi.length","channel_lo.length"]}` | Fuse two or more sibling params into ONE public knob: the member addresses leave the sweepable param space and `as` replaces them; the bound or swept value fans out to every member at bootstrap. Members must share one scalar kind and stay open (un-bound). |
|
||||||
|
|
||||||
Value forms are the typed-tag representations used everywhere in this
|
Value forms are the typed-tag representations used everywhere in this
|
||||||
family of artifacts:
|
family of artifacts:
|
||||||
@@ -78,6 +79,12 @@ up in `--params`/`--list-axes`, and no campaign axis and no `aura sweep
|
|||||||
--axis` can reopen it. `bind` is for a constant the strategy never varies;
|
--axis` can reopen it. `bind` is for a constant the strategy never varies;
|
||||||
leave a param unbound, as all three are here, to keep it open for sweeping.
|
leave a param unbound, as all three are here, to keep it open for sweeping.
|
||||||
|
|
||||||
|
A param therefore has three states: **open** (a sweepable axis), **bound**
|
||||||
|
(closed by a `bind` — gone from the axis namespace), and **ganged** (open, but
|
||||||
|
fused with its siblings under ONE public knob declared by a `gang` op; the
|
||||||
|
member addresses are unbindable and only the gang's own single-segment name —
|
||||||
|
wrapped like any knob, e.g. `graph.channel_length` — appears in `--list-axes`).
|
||||||
|
|
||||||
### Commands
|
### Commands
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -125,6 +132,10 @@ raw (--params, campaign axes): fast.length
|
|||||||
wrapped (--list-axes, --axis): graph.fast.length
|
wrapped (--list-axes, --axis): graph.fast.length
|
||||||
```
|
```
|
||||||
|
|
||||||
|
A ganged knob's raw address has one path segment less than a member address
|
||||||
|
would — it sits at the composite's own level, like a role name (e.g.
|
||||||
|
`channel_length`, not `channel_hi.length`) — and wraps identically (`graph.channel_length`).
|
||||||
|
|
||||||
`--content-id`, `--identity-id`, `--params`, and `graph register` all accept
|
`--content-id`, `--identity-id`, `--params`, and `graph register` all accept
|
||||||
**either** shape: the raw op-script array or an already-built `#155`
|
**either** shape: the raw op-script array or an already-built `#155`
|
||||||
blueprint envelope (object) — shape-discriminated automatically, so you
|
blueprint envelope (object) — shape-discriminated automatically, so you
|
||||||
|
|||||||
+11
-1
@@ -2093,6 +2093,11 @@ node type, edge semantics, or structural-axis kind) bumps `format_version` so an
|
|||||||
old reader refuses cleanly (`LoadError::UnsupportedVersion` / `UnknownNodeType`,
|
old reader refuses cleanly (`LoadError::UnsupportedVersion` / `UnknownNodeType`,
|
||||||
already green). The per-section required-flag scheme is deferred (no current
|
already green). The per-section required-flag scheme is deferred (no current
|
||||||
Tier-2 section to validate it; recorded on #156).
|
Tier-2 section to validate it; recorded on #156).
|
||||||
|
Pre-ship dormancy (#61, 2026-07-10): until the first external ship there are
|
||||||
|
no out-of-repo readers — reader and writer change atomically in one commit —
|
||||||
|
so the Tier-2 bump discipline is dormant and structurally-semantic additions
|
||||||
|
(the `gangs` section) land as additive-optional fields of v1; the first ship
|
||||||
|
consciously freezes v1, gangs included, and activates the bump discipline.
|
||||||
**Milestone delivered — 2026-06-30, cycle 0090 — the serializable format + loader + construction service.** A green end-to-end milestone fieldtest (`fieldtests/milestone-topology-as-data/`) proves the full author → serialize → load → construct → introspect → reproduce story from the public surface alone, 0 behavioural bugs; the round-trippable format (#155), the construction service (#157), and the forward-compat two-tier discipline (#156) all shipped. Polish filed forward: op-script grammar docs + a stale example (#163), the canonical trailing-newline / `Composite` value ergonomics (#164), CLI discoverability of `build`/`introspect` (#159).
|
**Milestone delivered — 2026-06-30, cycle 0090 — the serializable format + loader + construction service.** A green end-to-end milestone fieldtest (`fieldtests/milestone-topology-as-data/`) proves the full author → serialize → load → construct → introspect → reproduce story from the public surface alone, 0 behavioural bugs; the round-trippable format (#155), the construction service (#157), and the forward-compat two-tier discipline (#156) all shipped. Polish filed forward: op-script grammar docs + a stale example (#163), the canonical trailing-newline / `Composite` value ergonomics (#164), CLI discoverability of `build`/`introspect` (#159).
|
||||||
**Realization (2026-06-30, cycle 0092 — runs are built FROM blueprint-data, #165).**
|
**Realization (2026-06-30, cycle 0092 — runs are built FROM blueprint-data, #165).**
|
||||||
`aura run <blueprint.json>` now loads a serialized **signal** blueprint
|
`aura run <blueprint.json>` now loads a serialized **signal** blueprint
|
||||||
@@ -2195,7 +2200,7 @@ fixture from the scaffolder.
|
|||||||
**Op-script grammar (`aura graph build`, the #157 wire surface).** The construction
|
**Op-script grammar (`aura graph build`, the #157 wire surface).** The construction
|
||||||
op-script is a JSON **array of ops**, each object internally tagged by `"op"`,
|
op-script is a JSON **array of ops**, each object internally tagged by `"op"`,
|
||||||
replayed in order; nodes are referenced **by identifier**, ports as dotted
|
replayed in order; nodes are referenced **by identifier**, ports as dotted
|
||||||
`<identifier>.<port>`. The six verbs:
|
`<identifier>.<port>`. The seven verbs:
|
||||||
- `source` — `{"op":"source","role":<str>,"kind":<ScalarKind>}` — declare a root
|
- `source` — `{"op":"source","role":<str>,"kind":<ScalarKind>}` — declare a root
|
||||||
source role producing a base column of `kind`.
|
source role producing a base column of `kind`.
|
||||||
- `input` — `{"op":"input","role":<str>}` — declare a root input role (kind inferred
|
- `input` — `{"op":"input","role":<str>}` — declare a root input role (kind inferred
|
||||||
@@ -2212,6 +2217,11 @@ replayed in order; nodes are referenced **by identifier**, ports as dotted
|
|||||||
- `expose` — `{"op":"expose","from":<port>,"as":<str>}` — promote an interior output
|
- `expose` — `{"op":"expose","from":<port>,"as":<str>}` — promote an interior output
|
||||||
field to a boundary output, aliased by `as` — **the only verb that keeps `as`**
|
field to a boundary output, aliased by `as` — **the only verb that keeps `as`**
|
||||||
(a real alias, not a naming; contrast `add`'s `name`).
|
(a real alias, not a naming; contrast `add`'s `name`).
|
||||||
|
- `gang` — `{"op":"gang","as":"channel_length","into":["channel_hi.length","channel_lo.length"]}`
|
||||||
|
— fuse two or more sibling params into ONE public knob: the member addresses
|
||||||
|
leave the sweepable param space and `as` replaces them; the bound or swept
|
||||||
|
value fans out to every member at bootstrap. Members must share one scalar
|
||||||
|
kind and stay open (un-bound).
|
||||||
|
|
||||||
Value forms are the #155 representations: a `Scalar` bind value is the typed tag form
|
Value forms are the #155 representations: a `Scalar` bind value is the typed tag form
|
||||||
`{"I64":2}` / `{"F64":0.5}` / `{"Bool":true}` / `{"Timestamp":<i64>}`, and `kind` is
|
`{"I64":2}` / `{"F64":0.5}` / `{"Bool":true}` / `{"Timestamp":<i64>}`, and `kind` is
|
||||||
|
|||||||
+11
-1
@@ -119,6 +119,16 @@ A node re-evaluates only when ≥1 of its own inputs is fresh this cycle; otherw
|
|||||||
**Avoid:** deploy artifact, standalone binary
|
**Avoid:** deploy artifact, standalone binary
|
||||||
A statically-linked, versioned, frozen build, never hot-swapped (audit trail: this artifact = this commit). The general category; a `bot` is the specific case of a deployed strategy + broker.
|
A statically-linked, versioned, frozen build, never hot-swapped (audit trail: this artifact = this commit). The general category; a `bot` is the specific case of a deployed strategy + broker.
|
||||||
|
|
||||||
|
### gang
|
||||||
|
**Avoid:** —
|
||||||
|
A blueprint-level declaration that two or more sibling nodes' open params are
|
||||||
|
semantically one knob. The member addresses leave the blueprint's parameter
|
||||||
|
space; one gang address (a single path segment at the composite's level)
|
||||||
|
replaces them, and its value fans out to every member at bootstrap. Binding is
|
||||||
|
structural, not advisory — no consumer can bind a member independently. The
|
||||||
|
gang's name is a debug/axis symbol (identity-blind); its member coordinates
|
||||||
|
are identity-bearing. Declared by the `gang` op / `GraphBuilder::gang`.
|
||||||
|
|
||||||
### gross R
|
### gross R
|
||||||
**Avoid:** —
|
**Avoid:** —
|
||||||
The R-multiple outcome of the **signal alone** — per-trade R integrated by the R-evaluator with **no `cost model`** attached (the zero-cost baseline). `net R = gross R − cost-in-R` once a cost model is composed on.
|
The R-multiple outcome of the **signal alone** — per-trade R integrated by the R-evaluator with **no `cost model`** attached (the zero-cost baseline). `net R = gross R − cost-in-R` once a cost model is composed on.
|
||||||
@@ -293,7 +303,7 @@ The harness's structural parameterization — which strategy, instrument(s), bro
|
|||||||
|
|
||||||
### sweep
|
### sweep
|
||||||
**Avoid:** param-sweep, parameter sweep
|
**Avoid:** param-sweep, parameter sweep
|
||||||
An orchestration axis varying tuning params (grid or random) within a fixed structure. The inner, param-tuning loop, distinct from the structural experiment matrix. On a loaded blueprint every open knob (`--list-axes`) is **required** — a subset is refused with the missing knob named; pin an unwanted knob with a single-value axis (`--axis name=<one-value>`), there is no default. `aura sweep --axis` takes the `--list-axes`-printed, root-composite-wrapped name (e.g. `graph.fast.length`) — not the raw `param_space` name (`fast.length`) that `graph introspect --params` and a campaign document's axes use; the CLI strips exactly one leading wrapper segment (#210). The `aura sweep` CLI verb is now thin sugar over the `campaign document` path — its blueprint form (`<bp.json> --real`) translates to a generated, content-addressed campaign run through the one executor (#210); the built-in `--strategy` sweep surface was retired by #159 (its hard-wired harnesses removed) — sweep now runs from a blueprint + `--axis`, and a retired `--strategy` token falls to the generic usage error.
|
An orchestration axis varying tuning params (grid or random) within a fixed structure. The inner, param-tuning loop, distinct from the structural experiment matrix. On a loaded blueprint every open knob (`--list-axes`) is **required** — a subset is refused with the missing knob named; pin an unwanted knob with a single-value axis (`--axis name=<one-value>`), there is no default. `aura sweep --axis` takes the `--list-axes`-printed, root-composite-wrapped name (e.g. `graph.fast.length`) — not the raw `param_space` name (`fast.length`) that `graph introspect --params` and a campaign document's axes use; the CLI strips exactly one leading wrapper segment (#210). The `aura sweep` CLI verb is now thin sugar over the `campaign document` path — its blueprint form (`<bp.json> --real`) translates to a generated, content-addressed campaign run through the one executor (#210); the built-in `--strategy` sweep surface was retired by #159 (its hard-wired harnesses removed) — sweep now runs from a blueprint + `--axis`, and a retired `--strategy` token falls to the generic usage error. A ganged pair contributes ONE axis.
|
||||||
|
|
||||||
### tap
|
### tap
|
||||||
**Avoid:** —
|
**Avoid:** —
|
||||||
|
|||||||
Reference in New Issue
Block a user