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:
@@ -133,7 +133,8 @@ fn run_prints_json_and_exits_zero() {
|
||||
/// the topology hash into the content-addressed store and re-implementing
|
||||
/// aura's merge semantics. The manifest carries a `defaults` field — the bound
|
||||
/// 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"
|
||||
/// meaning: on a plain run nothing varied, so it stays `[]`, and every bound
|
||||
/// 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.
|
||||
assert!(line.contains("\"params\":[]"), "params stays empty on a plain run: {line}");
|
||||
|
||||
// The untouched bound values are stamped in `defaults`, wrap-prefixed with
|
||||
// the blueprint name and ordered by `bound_param_space()` (node-declaration
|
||||
// order: fast, slow, bias), each a self-describing tagged Scalar (C14).
|
||||
// The untouched bound values are stamped in `defaults`, RAW (#328) and
|
||||
// ordered by `bound_param_space()` (node-declaration order: fast, slow,
|
||||
// bias), each a self-describing tagged Scalar (C14).
|
||||
assert!(
|
||||
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}",
|
||||
);
|
||||
@@ -1688,15 +1689,15 @@ fn sweep_real_blueprint_member_lines_pin_the_dissolved_contract() {
|
||||
|
||||
// Odometer order: `fast.length` (first `--axis`, outer/slowest, typed RAW
|
||||
// per #328) x `slow.length` (second `--axis`, inner) ->
|
||||
// (2,8),(2,16),(4,8),(4,16). `report.manifest.params` itself is UNTOUCHED
|
||||
// by #328 (out of this cycle's scope — only the synthetic sweep route's
|
||||
// manifest moved to the raw frame) and still carries the WRAPPED
|
||||
// `param_space()` name the real/campaign member actually bound against.
|
||||
// (2,8),(2,16),(4,8),(4,16). `report.manifest.params` now records RAW
|
||||
// names on the real/campaign route too (this cycle's tidy fix, #328 batch
|
||||
// 2): a name-only reshaping at the mint seam, the member still binds
|
||||
// against the WRAPPED `param_space()` name internally.
|
||||
let expected: [[(&str, i64); 2]; 4] = [
|
||||
[("sma_signal.fast.length", 2), ("sma_signal.slow.length", 8)],
|
||||
[("sma_signal.fast.length", 2), ("sma_signal.slow.length", 16)],
|
||||
[("sma_signal.fast.length", 4), ("sma_signal.slow.length", 8)],
|
||||
[("sma_signal.fast.length", 4), ("sma_signal.slow.length", 16)],
|
||||
[("fast.length", 2), ("slow.length", 8)],
|
||||
[("fast.length", 2), ("slow.length", 16)],
|
||||
[("fast.length", 4), ("slow.length", 8)],
|
||||
[("fast.length", 4), ("slow.length", 16)],
|
||||
];
|
||||
|
||||
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
|
||||
/// 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
|
||||
/// `sma_signal.slow.length` bound), overriding only the `fast.length` axis and
|
||||
/// leaving `slow.length` bound — unlike the walk-forward reproduce sibling
|
||||
/// already-open one — `r_sma.json` (both `fast.length` and `slow.length` bound),
|
||||
/// overriding only the `fast.length` axis and leaving `slow.length` bound —
|
||||
/// unlike the walk-forward reproduce sibling
|
||||
/// (`aura_walkforward_over_a_blueprint_reproduces_bit_identically`), which
|
||||
/// overrides both axes over the same closed blueprint. The walk-forward
|
||||
/// synthetic route's own `--axis` intake is UNCHANGED by #328 (out of this
|
||||
/// cycle's scope — batch 1 is the sweep verb's raw switch only), so this axis
|
||||
/// is still typed WRAPPED here (`sma_signal.fast.length`); `override_paths`'s
|
||||
/// #328 fuzzy matching tolerates either shape, but the `SweepBinder` call this
|
||||
/// route drives (`blueprint_sweep_over`, untouched this cycle) still keys by
|
||||
/// the exact wrapped `param_space()` name, so a RAW-typed axis here would
|
||||
/// mismatch and misreport `UnknownKnob`.
|
||||
/// synthetic route's own `--axis` intake is now RAW (this cycle's tidy fix,
|
||||
/// #328): the CLI preflight refuses a wrapped name before this run ever starts
|
||||
/// (`refuse_wrapped_synthetic_axes`), and `blueprint_sweep_over`'s own
|
||||
/// `SweepBinder` call translates the RAW name onto its wrapped `param_space()`
|
||||
/// slot (`wrapped_name_of`) before keying by it.
|
||||
/// Modelled on that sibling (synthetic in-process walk-forward — no `--real`),
|
||||
/// NOT on `reproduce_real_sweep_family_re_derives_bit_identically`: a real-data
|
||||
/// 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 bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
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)
|
||||
.output()
|
||||
.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);
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// `BindError` renders as prose in the same register as its unknown-axis
|
||||
/// 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
|
||||
/// content-addressed WalkForward family that `aura reproduce`s bit-identically —
|
||||
/// 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
|
||||
/// (batch 1 is the sweep verb's raw switch only), so these axes stay WRAPPED.
|
||||
/// The synthetic walk-forward route's own `--axis` intake is now RAW (this
|
||||
/// cycle's tidy fix, #328 batch 2).
|
||||
#[test]
|
||||
fn aura_walkforward_over_a_blueprint_reproduces_bit_identically() {
|
||||
let cwd = temp_cwd("blueprint-walkforward-reproduce");
|
||||
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
let run = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["walkforward", &bp, "--axis", "sma_signal.fast.length=2,3",
|
||||
"--axis", "sma_signal.slow.length=4,6", "--name", "wfr"])
|
||||
.args(["walkforward", &bp, "--axis", "fast.length=2,3",
|
||||
"--axis", "slow.length=4,6", "--name", "wfr"])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.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 bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["walkforward", &bp, "--axis", "sma_signal.fast.length=2,3",
|
||||
"--axis", "sma_signal.slow.length=4,6", "--name", "wfx"])
|
||||
.args(["walkforward", &bp, "--axis", "fast.length=2,3",
|
||||
"--axis", "slow.length=4,6", "--name", "wfx"])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.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 bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["walkforward", &bp, "--axis", "sma_signal.fast.length=8",
|
||||
"--axis", "sma_signal.slow.length=8", "--name", "wfzero"])
|
||||
.args(["walkforward", &bp, "--axis", "fast.length=8",
|
||||
"--axis", "slow.length=8", "--name", "wfzero"])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.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 bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["walkforward", &bp, "--axis", "sma_signal.fast.length=2,3",
|
||||
"--axis", "sma_signal.slow.length=4,6", "--name", "wftrades"])
|
||||
.args(["walkforward", &bp, "--axis", "fast.length=2,3",
|
||||
"--axis", "slow.length=4,6", "--name", "wftrades"])
|
||||
.current_dir(&cwd)
|
||||
.output()
|
||||
.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
|
||||
// "a member panicked: …" and survives.
|
||||
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)
|
||||
.output()
|
||||
.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() {
|
||||
let bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
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"))
|
||||
.args(["walkforward", &bp, "--axis", "graph.fast.length=2,3"])
|
||||
.args(["walkforward", &bp, "--axis", "graph.nope.length=2,3"])
|
||||
.output()
|
||||
.expect("spawn aura walkforward (unknown axis)");
|
||||
assert_eq!(out.status.code(), Some(2), "an unknown axis must fail clean, not panic");
|
||||
|
||||
Reference in New Issue
Block a user