audit: #328 cycle close — manifests record raw on every route, walkforward intake refuses wrapped
Architect drift review (cycle a3785a6..b3b7115) found the cycle's raw switch incomplete on two high items; both fixed forward here rather than minuted as residue, since #319 builds on this namespace: - The real/campaign executor still minted WRAPPED manifest.params and manifest.defaults (AC2 overclaim in the feat commit body). The mint seams now reshape to raw on every route (member.rs raw_bound_defaults, runner.rs manifest_space mirror of the family.rs pattern); the fix surfaced a latent regression in persist_campaign_traces, which keyed point_from_params by exact wrapped name and is now translation-aware (same recipe as reproduce_family_in). Old on-disk families stay replayable (C29; reproduce reads both shapes). - The walkforward --axis intake silently accepted wrapped names — the silent alias fork 2 rejected. It now runs the same refuse_wrapped_synthetic_axes preflight as the plain synthetic sweep (new e2e: aura_walkforward_synthetic_blueprint_refuses_a_wrapped_form _axis_name); family builders translate raw names onto wrapped SweepBinder slots on all three entry points. C24's "retired from the surface" claim is thereby made true instead of softened. - Low items: stale graph_construct docstring updated; new graph_params_and_sweep_list_axes_are_line_identical pins the two discovery surfaces against format drift. - Bench infra: aura-bench's campaign_sweep surface seeded wrapped axis literals and was correctly refused by the new intake — seeds converted to raw; full bench rerun: all 5 surfaces fingerprint OK, deltas within load noise (report-only, no baseline update needed). Library-unit tests calling family builders directly keep wrapped input (valid: translation is idempotent on exact wrapped names; intake-level refusal is the user contract). Suite + clippy verified green post-fix. refs #328
This commit is contained in:
@@ -106,9 +106,9 @@ fn seed_blueprint(bin: &Path, dir: &Path, file: &str, name: &str) -> Result<Stri
|
|||||||
"sweep",
|
"sweep",
|
||||||
file,
|
file,
|
||||||
"--axis",
|
"--axis",
|
||||||
"sma_signal.fast.length=2,4",
|
"fast.length=2,4",
|
||||||
"--axis",
|
"--axis",
|
||||||
"sma_signal.slow.length=8,16",
|
"slow.length=8,16",
|
||||||
"--name",
|
"--name",
|
||||||
name,
|
name,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -615,10 +615,12 @@ pub fn introspect_cmd(cmd: crate::GraphIntrospectCmd, env: &aura_runner::project
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(target) = cmd.params.as_deref() {
|
} else if let Some(target) = cmd.params.as_deref() {
|
||||||
// --params <FILE|ID> (#196): the RAW composite param space — exactly the
|
// --params <FILE|ID> (#196): the RAW composite param space — the one
|
||||||
// namespace campaign axes are validated against (`validate_campaign_refs`
|
// namespace campaign axes are validated against
|
||||||
// checks the raw space; the wrapped `--list-axes` namespace on `aura sweep`
|
// (`validate_campaign_refs`) and `aura sweep --list-axes` prints
|
||||||
// is the sweep-verb view, not the campaign view).
|
// (#328: the wrapped `<blueprint>.<node>.<param>` form was retired —
|
||||||
|
// there is exactly one axis namespace now, so the two discovery
|
||||||
|
// surfaces agree line-for-line, see `params_lines`'s own doc comment).
|
||||||
match params_lines(target, env) {
|
match params_lines(target, env) {
|
||||||
Ok(s) => print!("{s}"),
|
Ok(s) => print!("{s}"),
|
||||||
Err(m) => {
|
Err(m) => {
|
||||||
|
|||||||
+14
-10
@@ -2523,10 +2523,11 @@ fn dispatch_walkforward(a: WalkforwardCmd, env: &aura_runner::project::Env) {
|
|||||||
exit_on_campaign_result(result);
|
exit_on_campaign_result(result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Synthetic in-process family path — unchanged (#220 non-goal); it
|
// Synthetic in-process family path (#220 non-goal, unchanged beyond
|
||||||
// persists no per-window taps, so `--trace` stays refused here (#224
|
// the #328 tidy below); it persists no per-window taps, so
|
||||||
// delivered only the `--real` campaign path above), mirroring
|
// `--trace` stays refused here (#224 delivered only the `--real`
|
||||||
// `dispatch_sweep`'s synthetic-arm refusal with its own named pointer.
|
// campaign path above), mirroring `dispatch_sweep`'s synthetic-arm
|
||||||
|
// refusal with its own named pointer.
|
||||||
if a.trace.is_some() {
|
if a.trace.is_some() {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"aura: --trace is not yet available on a synthetic walkforward (no --real); see #224"
|
"aura: --trace is not yet available on a synthetic walkforward (no --real); see #224"
|
||||||
@@ -2545,6 +2546,10 @@ fn dispatch_walkforward(a: WalkforwardCmd, env: &aura_runner::project::Env) {
|
|||||||
None => Selection::Argmax,
|
None => Selection::Argmax,
|
||||||
};
|
};
|
||||||
let name = a.name.clone().unwrap_or_else(|| "walkforward".to_string());
|
let name = a.name.clone().unwrap_or_else(|| "walkforward".to_string());
|
||||||
|
// #328: this route bypasses `validate_and_register_axes` (no
|
||||||
|
// project/registry involved, same gap the plain synthetic sweep
|
||||||
|
// arm had) — the same WRAPPED-name preflight closes it here too.
|
||||||
|
refuse_wrapped_synthetic_axes(&doc, env, &axes);
|
||||||
run_blueprint_walkforward(&doc, &axes, &name, DataSource::Synthetic, select, env);
|
run_blueprint_walkforward(&doc, &axes, &name, DataSource::Synthetic, select, env);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@@ -3627,9 +3632,8 @@ mod tests {
|
|||||||
/// two are disjoint by construction. Sweeping `fast.length` (RAW, #328) on
|
/// two are disjoint by construction. Sweeping `fast.length` (RAW, #328) on
|
||||||
/// the fully bound r_sma example leaves `slow.length`/`bias.scale`
|
/// the fully bound r_sma example leaves `slow.length`/`bias.scale`
|
||||||
/// untouched (they stay in `defaults`), while `fast.length` moves to
|
/// untouched (they stay in `defaults`), while `fast.length` moves to
|
||||||
/// `params` and drops out of `defaults` entirely. `manifest.params` keys
|
/// `params` and drops out of `defaults` entirely. Both `manifest.params`
|
||||||
/// are RAW (#328); `manifest.defaults` (a different field, `run`'s own
|
/// and `manifest.defaults` keys are RAW (#328, batch 2 flips defaults too).
|
||||||
/// stamp of untouched bound params — untouched this cycle) stays WRAPPED.
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sweep_override_excludes_the_reopened_default_from_the_manifest() {
|
fn sweep_override_excludes_the_reopened_default_from_the_manifest() {
|
||||||
let env = aura_runner::project::Env::std();
|
let env = aura_runner::project::Env::std();
|
||||||
@@ -3646,13 +3650,13 @@ mod tests {
|
|||||||
|
|
||||||
let default_names: Vec<&str> = manifest.defaults.iter().map(|(n, _)| n.as_str()).collect();
|
let default_names: Vec<&str> = manifest.defaults.iter().map(|(n, _)| n.as_str()).collect();
|
||||||
assert!(
|
assert!(
|
||||||
!default_names.contains(&"sma_signal.fast.length"),
|
!default_names.contains(&"fast.length"),
|
||||||
"the reopened default must not also appear in defaults: {default_names:?}"
|
"the reopened default must not also appear in defaults: {default_names:?}"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
default_names,
|
default_names,
|
||||||
["sma_signal.slow.length", "sma_signal.bias.scale"],
|
["slow.length", "bias.scale"],
|
||||||
"the untouched bound params stay in defaults: {default_names:?}"
|
"the untouched bound params stay in defaults, RAW: {default_names:?}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -133,7 +133,8 @@ fn run_prints_json_and_exits_zero() {
|
|||||||
/// the topology hash into the content-addressed store and re-implementing
|
/// the topology hash into the content-addressed store and re-implementing
|
||||||
/// aura's merge semantics. The manifest carries a `defaults` field — the bound
|
/// aura's merge semantics. The manifest carries a `defaults` field — the bound
|
||||||
/// params merged inside `PrimitiveBuilder::build` below every record-writing
|
/// params merged inside `PrimitiveBuilder::build` below every record-writing
|
||||||
/// layer — as wrap-prefixed `(name, Scalar)` pairs in `bound_param_space()`
|
/// layer — as RAW `(name, Scalar)` pairs (#328: `bound_param_space()`'s own
|
||||||
|
/// path-qualified name, no wrap-prefix concatenation) in `bound_param_space()`
|
||||||
/// order (the same coordinate `--axis` uses). `params` keeps its "what varied"
|
/// order (the same coordinate `--axis` uses). `params` keeps its "what varied"
|
||||||
/// meaning: on a plain run nothing varied, so it stays `[]`, and every bound
|
/// meaning: on a plain run nothing varied, so it stays `[]`, and every bound
|
||||||
/// value lands in `defaults` instead of vanishing from the record.
|
/// value lands in `defaults` instead of vanishing from the record.
|
||||||
@@ -150,12 +151,12 @@ fn run_manifest_stamps_untouched_bound_defaults() {
|
|||||||
// "what varied" is empty on a plain run — the bound defaults are NOT params.
|
// "what varied" is empty on a plain run — the bound defaults are NOT params.
|
||||||
assert!(line.contains("\"params\":[]"), "params stays empty on a plain run: {line}");
|
assert!(line.contains("\"params\":[]"), "params stays empty on a plain run: {line}");
|
||||||
|
|
||||||
// The untouched bound values are stamped in `defaults`, wrap-prefixed with
|
// The untouched bound values are stamped in `defaults`, RAW (#328) and
|
||||||
// the blueprint name and ordered by `bound_param_space()` (node-declaration
|
// ordered by `bound_param_space()` (node-declaration order: fast, slow,
|
||||||
// order: fast, slow, bias), each a self-describing tagged Scalar (C14).
|
// bias), each a self-describing tagged Scalar (C14).
|
||||||
assert!(
|
assert!(
|
||||||
line.contains(
|
line.contains(
|
||||||
r#""defaults":[["sma_signal.fast.length",{"I64":2}],["sma_signal.slow.length",{"I64":4}],["sma_signal.bias.scale",{"F64":0.5}]]"#
|
r#""defaults":[["fast.length",{"I64":2}],["slow.length",{"I64":4}],["bias.scale",{"F64":0.5}]]"#
|
||||||
),
|
),
|
||||||
"manifest must carry the untouched bound defaults: {line}",
|
"manifest must carry the untouched bound defaults: {line}",
|
||||||
);
|
);
|
||||||
@@ -1688,15 +1689,15 @@ fn sweep_real_blueprint_member_lines_pin_the_dissolved_contract() {
|
|||||||
|
|
||||||
// Odometer order: `fast.length` (first `--axis`, outer/slowest, typed RAW
|
// Odometer order: `fast.length` (first `--axis`, outer/slowest, typed RAW
|
||||||
// per #328) x `slow.length` (second `--axis`, inner) ->
|
// per #328) x `slow.length` (second `--axis`, inner) ->
|
||||||
// (2,8),(2,16),(4,8),(4,16). `report.manifest.params` itself is UNTOUCHED
|
// (2,8),(2,16),(4,8),(4,16). `report.manifest.params` now records RAW
|
||||||
// by #328 (out of this cycle's scope — only the synthetic sweep route's
|
// names on the real/campaign route too (this cycle's tidy fix, #328 batch
|
||||||
// manifest moved to the raw frame) and still carries the WRAPPED
|
// 2): a name-only reshaping at the mint seam, the member still binds
|
||||||
// `param_space()` name the real/campaign member actually bound against.
|
// against the WRAPPED `param_space()` name internally.
|
||||||
let expected: [[(&str, i64); 2]; 4] = [
|
let expected: [[(&str, i64); 2]; 4] = [
|
||||||
[("sma_signal.fast.length", 2), ("sma_signal.slow.length", 8)],
|
[("fast.length", 2), ("slow.length", 8)],
|
||||||
[("sma_signal.fast.length", 2), ("sma_signal.slow.length", 16)],
|
[("fast.length", 2), ("slow.length", 16)],
|
||||||
[("sma_signal.fast.length", 4), ("sma_signal.slow.length", 8)],
|
[("fast.length", 4), ("slow.length", 8)],
|
||||||
[("sma_signal.fast.length", 4), ("sma_signal.slow.length", 16)],
|
[("fast.length", 4), ("slow.length", 16)],
|
||||||
];
|
];
|
||||||
|
|
||||||
for i in 0..4 {
|
for i in 0..4 {
|
||||||
@@ -2015,18 +2016,16 @@ fn reproduce_real_sweep_family_re_derives_bit_identically() {
|
|||||||
|
|
||||||
/// Property (C18/C1 reproduce guarantee, #246): the same reproduce guarantee holds
|
/// Property (C18/C1 reproduce guarantee, #246): the same reproduce guarantee holds
|
||||||
/// when the swept axis names a BOUND param of a CLOSED starter blueprint, not an
|
/// when the swept axis names a BOUND param of a CLOSED starter blueprint, not an
|
||||||
/// already-open one — `r_sma.json` (both `sma_signal.fast.length` and
|
/// already-open one — `r_sma.json` (both `fast.length` and `slow.length` bound),
|
||||||
/// `sma_signal.slow.length` bound), overriding only the `fast.length` axis and
|
/// overriding only the `fast.length` axis and leaving `slow.length` bound —
|
||||||
/// leaving `slow.length` bound — unlike the walk-forward reproduce sibling
|
/// unlike the walk-forward reproduce sibling
|
||||||
/// (`aura_walkforward_over_a_blueprint_reproduces_bit_identically`), which
|
/// (`aura_walkforward_over_a_blueprint_reproduces_bit_identically`), which
|
||||||
/// overrides both axes over the same closed blueprint. The walk-forward
|
/// overrides both axes over the same closed blueprint. The walk-forward
|
||||||
/// synthetic route's own `--axis` intake is UNCHANGED by #328 (out of this
|
/// synthetic route's own `--axis` intake is now RAW (this cycle's tidy fix,
|
||||||
/// cycle's scope — batch 1 is the sweep verb's raw switch only), so this axis
|
/// #328): the CLI preflight refuses a wrapped name before this run ever starts
|
||||||
/// is still typed WRAPPED here (`sma_signal.fast.length`); `override_paths`'s
|
/// (`refuse_wrapped_synthetic_axes`), and `blueprint_sweep_over`'s own
|
||||||
/// #328 fuzzy matching tolerates either shape, but the `SweepBinder` call this
|
/// `SweepBinder` call translates the RAW name onto its wrapped `param_space()`
|
||||||
/// route drives (`blueprint_sweep_over`, untouched this cycle) still keys by
|
/// slot (`wrapped_name_of`) before keying by it.
|
||||||
/// the exact wrapped `param_space()` name, so a RAW-typed axis here would
|
|
||||||
/// mismatch and misreport `UnknownKnob`.
|
|
||||||
/// Modelled on that sibling (synthetic in-process walk-forward — no `--real`),
|
/// Modelled on that sibling (synthetic in-process walk-forward — no `--real`),
|
||||||
/// NOT on `reproduce_real_sweep_family_re_derives_bit_identically`: a real-data
|
/// NOT on `reproduce_real_sweep_family_re_derives_bit_identically`: a real-data
|
||||||
/// mint routes through the campaign executor (`CliMemberRunner::run_member` +
|
/// mint routes through the campaign executor (`CliMemberRunner::run_member` +
|
||||||
@@ -2044,7 +2043,7 @@ fn reproduce_family_with_overridden_bound_param_re_derives() {
|
|||||||
let cwd = temp_cwd("walkforward-reproduce-override");
|
let cwd = temp_cwd("walkforward-reproduce-override");
|
||||||
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
let run = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
let run = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||||
.args(["walkforward", &bp, "--axis", "sma_signal.fast.length=2,3", "--name", "wfo"])
|
.args(["walkforward", &bp, "--axis", "fast.length=2,3", "--name", "wfo"])
|
||||||
.current_dir(&cwd)
|
.current_dir(&cwd)
|
||||||
.output()
|
.output()
|
||||||
.expect("spawn aura walkforward");
|
.expect("spawn aura walkforward");
|
||||||
@@ -2412,6 +2411,44 @@ fn aura_sweep_synthetic_blueprint_refuses_a_wrapped_form_axis_name() {
|
|||||||
let _ = std::fs::remove_dir_all(&dir);
|
let _ = std::fs::remove_dir_all(&dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Property (#328, cycle-close tidy fix — walkforward's own sibling of
|
||||||
|
/// `aura_sweep_synthetic_blueprint_refuses_a_wrapped_form_axis_name`): the
|
||||||
|
/// SYNTHETIC (no `--real`) walk-forward route bypassed `validate_and_register_axes`
|
||||||
|
/// entirely (no project/registry touched) and silently accepted a WRAPPED
|
||||||
|
/// `<blueprint>.<node>.<param>` axis name — the gap this cycle closes by
|
||||||
|
/// wiring the same `refuse_wrapped_synthetic_axes` preflight the plain
|
||||||
|
/// synthetic sweep already had, ahead of `run_blueprint_walkforward`.
|
||||||
|
#[test]
|
||||||
|
fn aura_walkforward_synthetic_blueprint_refuses_a_wrapped_form_axis_name() {
|
||||||
|
let dir = temp_cwd("walkforward_synthetic_wrappedname_refusal");
|
||||||
|
let fixture = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
let out = Command::new(BIN)
|
||||||
|
.args(["walkforward", &fixture, "--axis", "sma_signal.fast.length=2,4"])
|
||||||
|
.current_dir(&dir)
|
||||||
|
.output()
|
||||||
|
.expect("spawn aura walkforward (synthetic, wrapped-form axis)");
|
||||||
|
assert_eq!(
|
||||||
|
out.status.code(),
|
||||||
|
Some(2),
|
||||||
|
"a wrapped-form axis name must be refused, not run: status={:?}",
|
||||||
|
out.status
|
||||||
|
);
|
||||||
|
let stderr = String::from_utf8_lossy(&out.stderr).into_owned();
|
||||||
|
assert!(
|
||||||
|
stderr.contains("axis \"sma_signal.fast.length\""),
|
||||||
|
"the refusal echoes exactly the name the user typed: {stderr}"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
stderr.contains("axis names are raw node.param paths") && stderr.contains("\"fast.length\""),
|
||||||
|
"translates the wrapped name to its raw candidate, naming the retirement: {stderr}"
|
||||||
|
);
|
||||||
|
// Data-free: no archive/store access at all — this route never touches a
|
||||||
|
// project store, so there is no `runs` dir to check for absence either
|
||||||
|
// way, but stdout must carry no member line (the walkforward never ran).
|
||||||
|
assert!(out.stdout.is_empty(), "a refused axis name must not print a member line: {out:?}");
|
||||||
|
let _ = std::fs::remove_dir_all(&dir);
|
||||||
|
}
|
||||||
|
|
||||||
/// Property (#247, #328 render-seam echo): every sweep-verb boundary
|
/// Property (#247, #328 render-seam echo): every sweep-verb boundary
|
||||||
/// `BindError` renders as prose in the same register as its unknown-axis
|
/// `BindError` renders as prose in the same register as its unknown-axis
|
||||||
/// sibling (`aura: axis <name>: names no param of this blueprint … — see
|
/// sibling (`aura: axis <name>: names no param of this blueprint … — see
|
||||||
@@ -4798,15 +4835,15 @@ fn aura_mc_over_a_blueprint_reproduces_bit_identically() {
|
|||||||
/// E2E (#173): an IS-refit walk-forward over a loaded blueprint persists a
|
/// E2E (#173): an IS-refit walk-forward over a loaded blueprint persists a
|
||||||
/// content-addressed WalkForward family that `aura reproduce`s bit-identically —
|
/// content-addressed WalkForward family that `aura reproduce`s bit-identically —
|
||||||
/// each OOS member's windowed slice + winner params are recovered from its manifest.
|
/// each OOS member's windowed slice + winner params are recovered from its manifest.
|
||||||
/// The synthetic walk-forward route's own `--axis` intake is unchanged by #328
|
/// The synthetic walk-forward route's own `--axis` intake is now RAW (this
|
||||||
/// (batch 1 is the sweep verb's raw switch only), so these axes stay WRAPPED.
|
/// cycle's tidy fix, #328 batch 2).
|
||||||
#[test]
|
#[test]
|
||||||
fn aura_walkforward_over_a_blueprint_reproduces_bit_identically() {
|
fn aura_walkforward_over_a_blueprint_reproduces_bit_identically() {
|
||||||
let cwd = temp_cwd("blueprint-walkforward-reproduce");
|
let cwd = temp_cwd("blueprint-walkforward-reproduce");
|
||||||
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
let run = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
let run = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||||
.args(["walkforward", &bp, "--axis", "sma_signal.fast.length=2,3",
|
.args(["walkforward", &bp, "--axis", "fast.length=2,3",
|
||||||
"--axis", "sma_signal.slow.length=4,6", "--name", "wfr"])
|
"--axis", "slow.length=4,6", "--name", "wfr"])
|
||||||
.current_dir(&cwd)
|
.current_dir(&cwd)
|
||||||
.output()
|
.output()
|
||||||
.expect("spawn aura walkforward");
|
.expect("spawn aura walkforward");
|
||||||
@@ -4830,8 +4867,8 @@ fn aura_walkforward_over_a_blueprint_persists_a_family() {
|
|||||||
let cwd = temp_cwd("blueprint-walkforward-persist");
|
let cwd = temp_cwd("blueprint-walkforward-persist");
|
||||||
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||||
.args(["walkforward", &bp, "--axis", "sma_signal.fast.length=2,3",
|
.args(["walkforward", &bp, "--axis", "fast.length=2,3",
|
||||||
"--axis", "sma_signal.slow.length=4,6", "--name", "wfx"])
|
"--axis", "slow.length=4,6", "--name", "wfx"])
|
||||||
.current_dir(&cwd)
|
.current_dir(&cwd)
|
||||||
.output()
|
.output()
|
||||||
.expect("spawn aura walkforward");
|
.expect("spawn aura walkforward");
|
||||||
@@ -4860,8 +4897,8 @@ fn aura_walkforward_all_zero_trade_windows_emit_one_note() {
|
|||||||
let cwd = temp_cwd("blueprint-walkforward-zero-trade-note");
|
let cwd = temp_cwd("blueprint-walkforward-zero-trade-note");
|
||||||
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||||
.args(["walkforward", &bp, "--axis", "sma_signal.fast.length=8",
|
.args(["walkforward", &bp, "--axis", "fast.length=8",
|
||||||
"--axis", "sma_signal.slow.length=8", "--name", "wfzero"])
|
"--axis", "slow.length=8", "--name", "wfzero"])
|
||||||
.current_dir(&cwd)
|
.current_dir(&cwd)
|
||||||
.output()
|
.output()
|
||||||
.expect("spawn aura walkforward (degenerate)");
|
.expect("spawn aura walkforward (degenerate)");
|
||||||
@@ -4892,8 +4929,8 @@ fn aura_walkforward_with_trades_emits_no_zero_trade_note() {
|
|||||||
let cwd = temp_cwd("blueprint-walkforward-traded-no-note");
|
let cwd = temp_cwd("blueprint-walkforward-traded-no-note");
|
||||||
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||||
.args(["walkforward", &bp, "--axis", "sma_signal.fast.length=2,3",
|
.args(["walkforward", &bp, "--axis", "fast.length=2,3",
|
||||||
"--axis", "sma_signal.slow.length=4,6", "--name", "wftrades"])
|
"--axis", "slow.length=4,6", "--name", "wftrades"])
|
||||||
.current_dir(&cwd)
|
.current_dir(&cwd)
|
||||||
.output()
|
.output()
|
||||||
.expect("spawn aura walkforward (trading)");
|
.expect("spawn aura walkforward (trading)");
|
||||||
@@ -4922,7 +4959,7 @@ fn aura_walkforward_synthetic_member_panic_is_contained() {
|
|||||||
// during member compile — the same fault class the real path records as
|
// during member compile — the same fault class the real path records as
|
||||||
// "a member panicked: …" and survives.
|
// "a member panicked: …" and survives.
|
||||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||||
.args(["walkforward", &bp, "--axis", "sma_signal.fast.length=0", "--name", "wfpanic"])
|
.args(["walkforward", &bp, "--axis", "fast.length=0", "--name", "wfpanic"])
|
||||||
.current_dir(&cwd)
|
.current_dir(&cwd)
|
||||||
.output()
|
.output()
|
||||||
.expect("spawn aura walkforward (poison member)");
|
.expect("spawn aura walkforward (poison member)");
|
||||||
@@ -5153,8 +5190,15 @@ fn aura_walkforward_over_a_blueprint_rejects_an_unknown_axis() {
|
|||||||
fn aura_walkforward_emits_an_unknown_axis_rejection_once() {
|
fn aura_walkforward_emits_an_unknown_axis_rejection_once() {
|
||||||
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
for attempt in 1..=20 {
|
for attempt in 1..=20 {
|
||||||
|
// "graph.nope.length" is dotted (multi-segment, like a real axis path)
|
||||||
|
// but matches neither the raw NOR the wrapped namespace (#328's own
|
||||||
|
// `refuse_wrapped_synthetic_axes` preflight, now ahead of this route
|
||||||
|
// too, would otherwise intercept a dotted name that happens to
|
||||||
|
// wrap-strip onto a real bound param, e.g. the retired
|
||||||
|
// `graph.fast.length` literal this test used to carry) — so it still
|
||||||
|
// reaches the genuine "names no param" rejection this test pins.
|
||||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||||
.args(["walkforward", &bp, "--axis", "graph.fast.length=2,3"])
|
.args(["walkforward", &bp, "--axis", "graph.nope.length=2,3"])
|
||||||
.output()
|
.output()
|
||||||
.expect("spawn aura walkforward (unknown axis)");
|
.expect("spawn aura walkforward (unknown axis)");
|
||||||
assert_eq!(out.status.code(), Some(2), "an unknown axis must fail clean, not panic");
|
assert_eq!(out.status.code(), Some(2), "an unknown axis must fail clean, not panic");
|
||||||
|
|||||||
@@ -660,9 +660,11 @@ fn register_refuses_undescribed_composites_end_to_end() {
|
|||||||
|
|
||||||
/// Property (#196, the campaign-axis namespace): `--params <FILE>` prints the
|
/// Property (#196, the campaign-axis namespace): `--params <FILE>` prints the
|
||||||
/// RAW composite param space — one `{name}:{kind:?}` line per open param, in
|
/// RAW composite param space — one `{name}:{kind:?}` line per open param, in
|
||||||
/// lowering order, WITHOUT the harness-wrap prefix `aura sweep --list-axes`
|
/// lowering order (#328: the one raw axis namespace — `aura sweep
|
||||||
/// shows. The open fixture leaves exactly the two SMA lengths unbound
|
/// --list-axes` prints the identical lines for the same blueprint, see
|
||||||
/// (`bias.scale` is bound in the document).
|
/// `graph_params_and_sweep_list_axes_are_line_identical` below). The open
|
||||||
|
/// fixture leaves exactly the two SMA lengths unbound (`bias.scale` is bound
|
||||||
|
/// in the document).
|
||||||
#[test]
|
#[test]
|
||||||
fn graph_params_lists_raw_axis_namespace() {
|
fn graph_params_lists_raw_axis_namespace() {
|
||||||
let dir = temp_cwd("params");
|
let dir = temp_cwd("params");
|
||||||
@@ -675,6 +677,29 @@ fn graph_params_lists_raw_axis_namespace() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Property (#328, cycle-close tidy fix): `graph introspect --params` and
|
||||||
|
/// `aura sweep --list-axes` are two independently-dispatched discovery
|
||||||
|
/// surfaces over the SAME raw axis namespace — the open pass shares one
|
||||||
|
/// wrap-strip convention, the bound pass shares one `bound_param_space()` +
|
||||||
|
/// `render_value` lexicon (`list_blueprint_axes`, main.rs; `params_lines`,
|
||||||
|
/// this crate). Pinned here as a same-fixture LINE EQUALITY (not just two
|
||||||
|
/// independently-asserted shapes) so the duplicated formatting cannot
|
||||||
|
/// silently drift apart across an edit to either surface.
|
||||||
|
#[test]
|
||||||
|
fn graph_params_and_sweep_list_axes_are_line_identical() {
|
||||||
|
let dir = temp_cwd("params-vs-list-axes");
|
||||||
|
let bp = fixture("r_sma_open.json");
|
||||||
|
let (params_out, params_err, params_code) =
|
||||||
|
run_in(&dir, &["graph", "introspect", "--params", &bp]);
|
||||||
|
assert_eq!(params_code, Some(0), "graph introspect --params: {params_out} {params_err}");
|
||||||
|
let (axes_out, axes_err, axes_code) = run_in(&dir, &["sweep", &bp, "--list-axes"]);
|
||||||
|
assert_eq!(axes_code, Some(0), "sweep --list-axes: {axes_out} {axes_err}");
|
||||||
|
assert_eq!(
|
||||||
|
params_out, axes_out,
|
||||||
|
"the two discovery surfaces must print byte-identical lines for the same blueprint"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Property (#196, file-mode --content-id): a blueprint FILE's printed content
|
/// Property (#196, file-mode --content-id): a blueprint FILE's printed content
|
||||||
/// id is exactly the store key `graph register` prints — the file is shape-
|
/// id is exactly the store key `graph register` prints — the file is shape-
|
||||||
/// discriminated from the op-list form and canonicalized by the blueprint rules.
|
/// discriminated from the op-list form and canonicalized by the blueprint rules.
|
||||||
|
|||||||
@@ -212,11 +212,11 @@ fn project_node_open_param_sweeps_and_reproduces() {
|
|||||||
let lines: Vec<&str> = stdout.lines().collect();
|
let lines: Vec<&str> = stdout.lines().collect();
|
||||||
assert_eq!(lines.len(), 2, "one member line per factor point: {stdout}");
|
assert_eq!(lines.len(), 2, "one member line per factor point: {stdout}");
|
||||||
|
|
||||||
// Each member's manifest carries the swept PROJECT-node param binding under
|
// Each member's manifest carries the swept PROJECT-node param binding
|
||||||
// its wrapped name (the real/campaign route's own `manifest.params` is
|
// under its RAW name (this cycle's tidy fix, #328 batch 2: the
|
||||||
// unchanged by #328 — batch 1 is the synthetic sweep route's manifest
|
// real/campaign route's own `manifest.params` mints raw too now) — the
|
||||||
// only) — the load-bearing proof that it is MY node's knob that varied
|
// load-bearing proof that it is MY node's knob that varied across the
|
||||||
// across the family, not some incidental std axis.
|
// family, not some incidental std axis.
|
||||||
for (line, factor) in lines.iter().zip([0.5_f64, 1.0]) {
|
for (line, factor) in lines.iter().zip([0.5_f64, 1.0]) {
|
||||||
let v: serde_json::Value = serde_json::from_str(line).expect("member line parses as JSON");
|
let v: serde_json::Value = serde_json::from_str(line).expect("member line parses as JSON");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -229,7 +229,7 @@ fn project_node_open_param_sweeps_and_reproduces() {
|
|||||||
.expect("manifest.params is an array");
|
.expect("manifest.params is an array");
|
||||||
let bound = params
|
let bound = params
|
||||||
.iter()
|
.iter()
|
||||||
.find(|p| p[0].as_str() == Some("scaled_signal.gain.factor"))
|
.find(|p| p[0].as_str() == Some("gain.factor"))
|
||||||
.and_then(|p| p[1]["F64"].as_f64());
|
.and_then(|p| p[1]["F64"].as_f64());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bound,
|
bound,
|
||||||
|
|||||||
@@ -96,6 +96,24 @@ pub fn classify_axis_intake(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Translate one RAW axis name onto the ONE wrapped `space` slot it
|
||||||
|
/// suffix-matches ([`raw_matches_wrapped`]), falling back to `raw` unchanged
|
||||||
|
/// when no wrapped slot matches — the caller's own downstream name
|
||||||
|
/// resolution (e.g. the sweep terminal's `UnknownKnob`) surfaces that case,
|
||||||
|
/// never panicked here. #328: the shared translation `blueprint_sweep_family`
|
||||||
|
/// established for its `SweepBinder::axis` call, extracted so
|
||||||
|
/// `blueprint_sweep_over`/`validate_axis_grid` (the walkforward synthetic
|
||||||
|
/// route's own binder calls, which key by the same exact wrapped
|
||||||
|
/// `param_space()` name) can translate a RAW `--axis` name too instead of
|
||||||
|
/// requiring the retired wrapped form.
|
||||||
|
pub fn wrapped_name_of(raw: &str, space: &[ParamSpec]) -> String {
|
||||||
|
space
|
||||||
|
.iter()
|
||||||
|
.find(|p| raw_matches_wrapped(raw, &p.name))
|
||||||
|
.map(|p| p.name.clone())
|
||||||
|
.unwrap_or_else(|| raw.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
/// Suffix-join each raw campaign axis onto exactly one wrapped param
|
/// Suffix-join each raw campaign axis onto exactly one wrapped param
|
||||||
/// ([`raw_matches_wrapped`] — wrapped == raw, or stripping the wrapper's one
|
/// ([`raw_matches_wrapped`] — wrapped == raw, or stripping the wrapper's one
|
||||||
/// node segment yields raw), then require every wrapped slot to be covered.
|
/// node segment yields raw), then require every wrapped slot to be covered.
|
||||||
|
|||||||
@@ -408,11 +408,17 @@ fn validate_axis_grid(
|
|||||||
) -> Result<(), BindError> {
|
) -> Result<(), BindError> {
|
||||||
let overrides = override_paths(axes, raw_space, probe_signal).map_err(BindError::UnknownKnob)?;
|
let overrides = override_paths(axes, raw_space, probe_signal).map_err(BindError::UnknownKnob)?;
|
||||||
let probe = blueprint_axis_probe_reopened(doc, env, &overrides);
|
let probe = blueprint_axis_probe_reopened(doc, env, &overrides);
|
||||||
|
let space = probe.param_space();
|
||||||
|
// #328: `axes` names are RAW (refused at the walkforward dispatch-boundary
|
||||||
|
// preflight ahead of this call, mirroring the plain sweep verb) — translate
|
||||||
|
// each onto the ONE wrapped `space` slot it suffix-matches
|
||||||
|
// (`wrapped_name_of`, the same convention `blueprint_sweep_family` feeds its
|
||||||
|
// own `SweepBinder`) before this terminal's exact-name resolution.
|
||||||
let mut iter = axes.iter();
|
let mut iter = axes.iter();
|
||||||
let (first_name, first_vals) = iter.next().expect("a blueprint walk-forward declares >= 1 axis");
|
let (first_name, first_vals) = iter.next().expect("a blueprint walk-forward declares >= 1 axis");
|
||||||
let mut binder = probe.axis(first_name, first_vals.clone());
|
let mut binder = probe.axis(&crate::axes::wrapped_name_of(first_name, &space), first_vals.clone());
|
||||||
for (n, vals) in iter {
|
for (n, vals) in iter {
|
||||||
binder = binder.axis(n, vals.clone());
|
binder = binder.axis(&crate::axes::wrapped_name_of(n, &space), vals.clone());
|
||||||
}
|
}
|
||||||
binder.sweep_with_lattice(|_| axis_grid_probe_report()).map(|_| ())
|
binder.sweep_with_lattice(|_| axis_grid_probe_report()).map(|_| ())
|
||||||
}
|
}
|
||||||
@@ -582,11 +588,16 @@ pub fn blueprint_sweep_over(
|
|||||||
let overrides = override_paths(axes, &raw_space, &probe_signal).map_err(BindError::UnknownKnob)?;
|
let overrides = override_paths(axes, &raw_space, &probe_signal).map_err(BindError::UnknownKnob)?;
|
||||||
let probe = blueprint_axis_probe_reopened(doc, env, &overrides);
|
let probe = blueprint_axis_probe_reopened(doc, env, &overrides);
|
||||||
let space = probe.param_space();
|
let space = probe.param_space();
|
||||||
|
// #328: `axes` names are RAW (refused at the walkforward dispatch-boundary
|
||||||
|
// preflight ahead of the caller, mirroring the plain sweep verb) —
|
||||||
|
// translate each onto the ONE wrapped `space` slot it suffix-matches
|
||||||
|
// (`wrapped_name_of`, the same convention `blueprint_sweep_family` feeds
|
||||||
|
// its own `SweepBinder`) before this terminal's exact-name resolution.
|
||||||
let mut iter = axes.iter();
|
let mut iter = axes.iter();
|
||||||
let (first_name, first_vals) = iter.next().expect("a blueprint walk-forward declares >= 1 axis");
|
let (first_name, first_vals) = iter.next().expect("a blueprint walk-forward declares >= 1 axis");
|
||||||
let mut binder = probe.axis(first_name, first_vals.clone());
|
let mut binder = probe.axis(&crate::axes::wrapped_name_of(first_name, &space), first_vals.clone());
|
||||||
for (n, vals) in iter {
|
for (n, vals) in iter {
|
||||||
binder = binder.axis(n, vals.clone());
|
binder = binder.axis(&crate::axes::wrapped_name_of(n, &space), vals.clone());
|
||||||
}
|
}
|
||||||
let faults: Mutex<Vec<(Vec<Cell>, String)>> = Mutex::new(Vec::new());
|
let faults: Mutex<Vec<(Vec<Cell>, String)>> = Mutex::new(Vec::new());
|
||||||
let (family, lattice) = binder.sweep_with_lattice(|point| {
|
let (family, lattice) = binder.sweep_with_lattice(|point| {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use std::collections::BTreeMap;
|
|||||||
use aura_core::{Scalar, Timestamp};
|
use aura_core::{Scalar, Timestamp};
|
||||||
use aura_engine::{Composite, Harness, MeasurementReport, RunManifest};
|
use aura_engine::{Composite, Harness, MeasurementReport, RunManifest};
|
||||||
|
|
||||||
use crate::member::{key_supply, resolve_run_data, wrapped_bound_defaults, RunData};
|
use crate::member::{key_supply, raw_bound_defaults, resolve_run_data, RunData};
|
||||||
use crate::project::Env;
|
use crate::project::Env;
|
||||||
use crate::tap_plan::{bind_tap_plan, TapPlan};
|
use crate::tap_plan::{bind_tap_plan, TapPlan};
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ pub fn run_measurement(
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
let names: Vec<String> = signal.param_space().iter().map(|p| p.name.clone()).collect();
|
let names: Vec<String> = signal.param_space().iter().map(|p| p.name.clone()).collect();
|
||||||
let defaults = wrapped_bound_defaults(&signal);
|
let defaults = raw_bound_defaults(&signal);
|
||||||
let (sources, window, _pip_size) = resolve_run_data(&data, env, &binding);
|
let (sources, window, _pip_size) = resolve_run_data(&data, env, &binding);
|
||||||
|
|
||||||
// Compile the signal DIRECTLY — no wrap_r, no broker/executor/eq-ex-r sinks.
|
// Compile the signal DIRECTLY — no wrap_r, no broker/executor/eq-ex-r sinks.
|
||||||
|
|||||||
@@ -82,22 +82,20 @@ pub fn sim_optimal_manifest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The wrap-prefixed BOUND-param defaults of `signal` (#249): every
|
/// The RAW-namespace BOUND-param defaults of `signal` (#249/#328): every
|
||||||
/// `bound_param_space()` entry as a `(<signal.name()>.<param>, value)` pair, in
|
/// `bound_param_space()` entry as a `(<param>, value)` pair, already RAW
|
||||||
/// `bound_param_space()` order — the `RunManifest.defaults` field. Must be read
|
/// (`bound_param_space()`'s own path-qualified name, #203 — no wrap-prefix
|
||||||
/// off `signal` BEFORE it is consumed by `wrap_r`/reopening: an axis-reopened
|
/// concatenation), in `bound_param_space()` order — the `RunManifest.defaults`
|
||||||
/// bound param has already left `bound_param_space()` by construction
|
/// field. Must be read off `signal` BEFORE it is consumed by
|
||||||
/// (`Composite::reopen` forgets the bound value), so a caller that reopens
|
/// `wrap_r`/reopening: an axis-reopened bound param has already left
|
||||||
/// overrides before calling this naturally excludes them — no separate filter
|
/// `bound_param_space()` by construction (`Composite::reopen` forgets the
|
||||||
/// needed. Same prefixing rule as `wrapped_bound_names`, kept separate because
|
/// bound value), so a caller that reopens overrides before calling this
|
||||||
/// that helper discards the value this one needs.
|
/// naturally excludes them — no separate filter needed. Contrast
|
||||||
pub fn wrapped_bound_defaults(signal: &Composite) -> Vec<(String, Scalar)> {
|
/// `wrapped_bound_names`, which DOES wrap-prefix (it matches against the
|
||||||
let prefix = format!("{}.", signal.name());
|
/// WRAPPED open `param_space()`, a different coordinate system than this
|
||||||
signal
|
/// manifest-recording helper needs).
|
||||||
.bound_param_space()
|
pub fn raw_bound_defaults(signal: &Composite) -> Vec<(String, Scalar)> {
|
||||||
.into_iter()
|
signal.bound_param_space().into_iter().map(|b| (b.name, b.value)).collect()
|
||||||
.map(|b| (format!("{prefix}{}", b.name), b.value))
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The honest broker label for the dual-tap r-sma harness: it runs a RiskExecutor
|
/// The honest broker label for the dual-tap r-sma harness: it runs a RiskExecutor
|
||||||
@@ -506,7 +504,7 @@ pub fn run_signal_r(
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|p| p.name.clone())
|
.map(|p| p.name.clone())
|
||||||
.collect();
|
.collect();
|
||||||
let defaults = wrapped_bound_defaults(&signal); // read before `signal` is consumed below
|
let defaults = raw_bound_defaults(&signal); // read before `signal` is consumed below
|
||||||
let (tx_eq, rx_eq) = mpsc::channel();
|
let (tx_eq, rx_eq) = mpsc::channel();
|
||||||
let (tx_ex, rx_ex) = mpsc::channel();
|
let (tx_ex, rx_ex) = mpsc::channel();
|
||||||
let (tx_r, rx_r) = mpsc::channel();
|
let (tx_r, rx_r) = mpsc::channel();
|
||||||
@@ -580,7 +578,7 @@ pub fn run_blueprint_member(
|
|||||||
cost: &[aura_research::CostSpec],
|
cost: &[aura_research::CostSpec],
|
||||||
instrument: &str,
|
instrument: &str,
|
||||||
) -> RunReport {
|
) -> RunReport {
|
||||||
let defaults = wrapped_bound_defaults(&signal); // read before `signal` is consumed below
|
let defaults = raw_bound_defaults(&signal); // read before `signal` is consumed below
|
||||||
let (tx_eq, rx_eq) = mpsc::channel();
|
let (tx_eq, rx_eq) = mpsc::channel();
|
||||||
let (tx_ex, rx_ex) = mpsc::channel();
|
let (tx_ex, rx_ex) = mpsc::channel();
|
||||||
let (tx_r, rx_r) = mpsc::channel();
|
let (tx_r, rx_r) = mpsc::channel();
|
||||||
|
|||||||
@@ -143,15 +143,17 @@ pub fn reproduce_family_in(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
let space = crate::member::wrap_r(reload()?, tx_eq, tx_ex, tx_r, tx_req, stop, true, SYNTHETIC_PIP_SIZE, &binding, None).param_space();
|
let space = crate::member::wrap_r(reload()?, tx_eq, tx_ex, tx_r, tx_req, stop, true, SYNTHETIC_PIP_SIZE, &binding, None).param_space();
|
||||||
// #328: `stored.manifest.params` may be RAW (a `Sweep` family minted by
|
// #328: `stored.manifest.params` is RAW on every mint route now (the
|
||||||
// `blueprint_sweep_family`, whose manifest now records the raw campaign
|
// synthetic sweep family and, since this cycle's tidy fix, the
|
||||||
// namespace) or WRAPPED (every other route, unchanged this cycle) —
|
// real/campaign route too) — but a family minted before this fix may
|
||||||
// `reproduce_family_in` reproduces either generically, so each recorded
|
// still carry WRAPPED names on disk (C29: no retroactive rewrite of a
|
||||||
// name is translated onto its matching WRAPPED `space` slot
|
// registered artifact), so `reproduce_family_in` reproduces either
|
||||||
// (`raw_matches_wrapped`, tolerant of both shapes) before
|
// shape generically: each recorded name is translated onto its
|
||||||
// `point_from_params`, which still keys by the exact wrapped name.
|
// matching WRAPPED `space` slot (`raw_matches_wrapped`, tolerant of
|
||||||
// Non-axis stamps (`stop_length`, `cost[k].<knob>`) carry no wrap
|
// both shapes) before `point_from_params`, which still keys by the
|
||||||
// segment either way and translate to themselves (no `space` hit).
|
// exact wrapped name. Non-axis stamps (`stop_length`, `cost[k].<knob>`)
|
||||||
|
// carry no wrap segment either way and translate to themselves (no
|
||||||
|
// `space` hit).
|
||||||
let wrapped_params: Vec<(String, Scalar)> = stored
|
let wrapped_params: Vec<(String, Scalar)> = stored
|
||||||
.manifest
|
.manifest
|
||||||
.params
|
.params
|
||||||
|
|||||||
@@ -116,6 +116,21 @@ impl MemberRunner for DefaultMemberRunner<'_> {
|
|||||||
.param_space();
|
.param_space();
|
||||||
let point = bind_axes(&space, &cell.strategy_id, params)?;
|
let point = bind_axes(&space, &cell.strategy_id, params)?;
|
||||||
let signal = reopen_all(signal, &overrides);
|
let signal = reopen_all(signal, &overrides);
|
||||||
|
// manifest.params records RAW names (#328: the real/campaign route was
|
||||||
|
// the one mint left wrapped after the synthetic route's own switch) —
|
||||||
|
// a name-only reshaping of `space` (order/kind untouched) fed to
|
||||||
|
// `run_blueprint_member`, which only zips it against `point` BY
|
||||||
|
// POSITION (`zip_params`) — never re-resolves by name — so renaming
|
||||||
|
// here is purely the manifest's own namespace, with no effect on
|
||||||
|
// member resolution (`space` itself stays wrapped for `bind_axes`
|
||||||
|
// above, which still keys by the exact wrapped `param_space()` name).
|
||||||
|
let manifest_space: Vec<aura_core::ParamSpec> = space
|
||||||
|
.iter()
|
||||||
|
.map(|p| aura_core::ParamSpec {
|
||||||
|
name: crate::axes::wrapped_to_raw_axis(&p.name).to_string(),
|
||||||
|
kind: p.kind,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
// The member's resolved input binding (campaign data.bindings
|
// The member's resolved input binding (campaign data.bindings
|
||||||
// overrides win over name defaults). A refusal is a member fault,
|
// overrides win over name defaults). A refusal is a member fault,
|
||||||
@@ -173,7 +188,7 @@ impl MemberRunner for DefaultMemberRunner<'_> {
|
|||||||
let mut report = run_blueprint_member(
|
let mut report = run_blueprint_member(
|
||||||
signal,
|
signal,
|
||||||
&point,
|
&point,
|
||||||
&space,
|
&manifest_space,
|
||||||
sources,
|
sources,
|
||||||
(from, to),
|
(from, to),
|
||||||
0,
|
0,
|
||||||
@@ -490,7 +505,28 @@ pub fn persist_campaign_traces(
|
|||||||
// is the member report's own `manifest.window` — already
|
// is the member report's own `manifest.window` — already
|
||||||
// epoch-ns (`run_blueprint_member` stamped the post-seam
|
// epoch-ns (`run_blueprint_member` stamped the post-seam
|
||||||
// bounds), so no second ms->ns crossing here.
|
// bounds), so no second ms->ns crossing here.
|
||||||
let point = point_from_params(&space, &member_report.manifest.params)?;
|
//
|
||||||
|
// #328: `member_report.manifest.params` is RAW (this cycle's tidy
|
||||||
|
// fix put the real/campaign mint on the raw frame too), but
|
||||||
|
// `point_from_params` still keys by the exact WRAPPED `space`
|
||||||
|
// name — translate each recorded name onto its matching wrapped
|
||||||
|
// slot first (`raw_matches_wrapped`, tolerant of either shape, the
|
||||||
|
// same `reproduce_family_in` recipe), never re-resolving by a
|
||||||
|
// renamed identity.
|
||||||
|
let wrapped_params: Vec<(String, Scalar)> = member_report
|
||||||
|
.manifest
|
||||||
|
.params
|
||||||
|
.iter()
|
||||||
|
.map(|(n, v)| {
|
||||||
|
let wrapped = space
|
||||||
|
.iter()
|
||||||
|
.find(|p| crate::axes::raw_matches_wrapped(n, &p.name))
|
||||||
|
.map(|p| p.name.clone())
|
||||||
|
.unwrap_or_else(|| n.clone());
|
||||||
|
(wrapped, *v)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let point = point_from_params(&space, &wrapped_params)?;
|
||||||
let (from, to) = member_report.manifest.window;
|
let (from, to) = member_report.manifest.window;
|
||||||
let no_data = || {
|
let no_data = || {
|
||||||
format!(
|
format!(
|
||||||
|
|||||||
Reference in New Issue
Block a user