fix(aura-cli, aura-runner): review findings — duplicate --override refusal, live discovery prose, neutral JSON refusal
In-cycle fixes for the independent review of the #319 package, RED-first for both Importants. A repeated --override path now refuses at the shared lexer (exit 2, path named) instead of panicking exit 101 through reopen_all — which also un-misattributes the campaign leg's collision prose for duplicates. override_paths' refusal points at aura graph introspect --params (the retired --list-axes reference swept from every live comment, one explicitly-historical mention remains). exec classifies file targets through a Result-bearing peek: unparseable JSON refuses neutrally before any leg is chosen. RunPresentation (single-variant) removed; run_campaign_returning's contract note names its one deliberate usage-class exit; C14's routing sentence and C20's family-builder paragraph amended to current truth (superseded text to the history sidecars); bench error strings and deletion blank-line runs tidied. refs #319
This commit is contained in:
@@ -203,7 +203,6 @@ fn sweep_campaign_doc(
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/// Property (ported onto a one-cell campaign document, #319 Task 8 — the
|
||||
/// retired `run --real` verb's own hard exit-1/no-stdout contract does not
|
||||
/// survive the campaign path's #272 contained-fault model, but the pip-honesty
|
||||
@@ -391,9 +390,6 @@ fn run_real_nonvetted_symbol_resolves_pip_from_sidecar() {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Note (#159 cut 4 collateral): `--trace` on a single `aura run`/`aura sweep`
|
||||
// is no longer reachable from any CLI surface — the built-in default that
|
||||
// accepted it retired with the last two PIP strategies, and the blueprint
|
||||
@@ -736,8 +732,6 @@ fn no_args_prints_usage_and_exits_two() {
|
||||
assert!(stderr.contains("Usage"), "stderr was: {stderr:?}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Property: `aura graph` emits a single self-contained HTML page — the
|
||||
/// interactive pin-graph viewer with the deterministic model inlined and the
|
||||
/// vendored Graphviz-WASM + pan-zoom inlined (no remote fetch). Driven through
|
||||
@@ -878,7 +872,6 @@ fn graph_with_bad_blueprint_arg_is_a_usage_fault_not_a_silent_sample() {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn runs_families_list_and_per_family_rank_across_invocations() {
|
||||
// Ported onto a one-cell campaign document run twice (#319 Task 8): two
|
||||
@@ -1328,7 +1321,6 @@ fn run_real_ohlc_channel_example_end_to_end_hostless() {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// The dissolved real-data sweep + reproduce over the OHLC example (#231
|
||||
/// acceptance), ported onto a one-cell campaign document (#319 Task 8 —
|
||||
/// `--list-axes` itself is dead flag surface, retired with `sweep`; its
|
||||
@@ -1755,24 +1747,27 @@ fn reproduce_real_sweep_family_re_derives_bit_identically() {
|
||||
/// 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 now RAW (this cycle's tidy fix,
|
||||
/// #328): the CLI preflight refuses a wrapped name before this run ever starts
|
||||
/// overrides both axes over the same closed blueprint. Originally modelled on
|
||||
/// that sibling (the retired synthetic in-process walk-forward dispatch verb —
|
||||
/// no `--real`), NOT on `reproduce_real_sweep_family_re_derives_bit_identically`:
|
||||
/// a real-data mint routed through the campaign executor
|
||||
/// (`CliMemberRunner::run_member` + `validate_and_register_axes`), neither of
|
||||
/// which carried any #246 override threading — a gap outside that task's file
|
||||
/// scope (`blueprint_sweep_over`, `reproduce_family_in`) and spanning all four
|
||||
/// (then-live) dispatch verbs, so exercising it there would have silently
|
||||
/// expanded into unrelated, unreviewed surface. Back when the synthetic
|
||||
/// walk-forward dispatch verb existed, its `--axis` intake was RAW (#328): the
|
||||
/// CLI preflight refused a wrapped name before the run ever started
|
||||
/// (`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` +
|
||||
/// `validate_and_register_axes`), neither of which carries any #246 override
|
||||
/// threading — a gap outside this task's file scope (`blueprint_sweep_over`,
|
||||
/// `reproduce_family_in`) and spanning all four dispatch verbs, so exercising
|
||||
/// it here would silently expand into unrelated, unreviewed surface. The
|
||||
/// synthetic walk-forward path exercises exactly the threaded functions:
|
||||
/// `blueprint_sweep_over` (via `blueprint_walkforward_family`) re-opens
|
||||
/// `fast.length` for the per-window IS re-fit, and
|
||||
/// `reproduce_family_in` re-opens it again from the recorded manifest params
|
||||
/// so the re-derivation resolves against the same space the mint used.
|
||||
/// `SweepBinder` call translated the RAW name onto its wrapped `param_space()`
|
||||
/// slot (`wrapped_name_of`) before keying by it. That path exercised exactly
|
||||
/// the threaded functions: `blueprint_sweep_over` (via
|
||||
/// `blueprint_walkforward_family`) re-opened `fast.length` for the per-window
|
||||
/// IS re-fit, and `reproduce_family_in` re-opened it again from the recorded
|
||||
/// manifest params so the re-derivation resolved against the same space the
|
||||
/// mint used. #319 Task 8 ported this test onto the campaign
|
||||
/// `std::walk_forward` stage (see the in-body port note below); the property
|
||||
/// pinned here is unchanged.
|
||||
#[test]
|
||||
fn reproduce_family_with_overridden_bound_param_re_derives() {
|
||||
// Ported onto a `[std::grid, std::walk_forward]` campaign document over
|
||||
@@ -1939,7 +1934,6 @@ fn reproduce_real_walkforward_family_does_not_panic() {
|
||||
// fixture at a single axis value instead.)
|
||||
const HL_SIGNAL_OPEN_BLUEPRINT: &str = r#"{"format_version":1,"blueprint":{"name":"hl_signal","doc":"high/low SMA difference clamped into a directional bias","nodes":[{"primitive":{"type":"SMA","name":"fast"}},{"primitive":{"type":"SMA","name":"slow"}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"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}],"input_roles":[{"name":"high","targets":[{"node":0,"slot":0}],"source":"F64"},{"name":"low","targets":[{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}}"#;
|
||||
|
||||
|
||||
// Note (#319 Task 8, disposition deviation): `walkforward_synthetic_refuses_a_
|
||||
// multi_column_blueprint_before_data_access` (the synthetic, no-`--real`
|
||||
// exit-process arm) retired rather than ported — it is a byte-identical twin
|
||||
@@ -2042,12 +2036,6 @@ fn sweep_real_multi_column_blueprint_opens_high_and_low_columns() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Property (issue #246, task 5): the dissolved real-data `aura sweep`
|
||||
/// campaign path accepts an `--axis` naming a BOUND param of a CLOSED
|
||||
/// blueprint, not only an already-open one. Tasks 3-4 threaded this override
|
||||
@@ -2168,11 +2156,6 @@ fn sweep_over_a_bound_override_matches_the_historical_open_bind() {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Note (#159 cut 4 collateral): this removes the `aura chart <name>`
|
||||
// CLI-integration tests (single-run and family overlay/tap-filter/meta, the
|
||||
// cross-kind write-guard collision + same-kind-overwrite pair) — every one of
|
||||
@@ -2188,12 +2171,6 @@ fn sweep_over_a_bound_override_matches_the_historical_open_bind() {
|
||||
|
||||
// --- `aura run --harness <name>` selector (iter-3 Task 3) --------------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Property: `aura generalize` grades a single r-sma candidate across two
|
||||
/// instruments — its stdout carries the per-instrument breakdown + the worst-case
|
||||
/// floor + the sign-agreement, then the persisted CrossInstrument family handle.
|
||||
@@ -2307,17 +2284,20 @@ fn generalize_real_e2e_pins_the_exact_current_grade() {
|
||||
assert_eq!(per[1][1].as_f64(), Some(0.005795903617609842), "USDJPY expectancy R: {record_line}");
|
||||
}
|
||||
|
||||
/// Property (#220 generalize vertical): `aura generalize` grades ANY sweepable
|
||||
/// blueprint, not just the historical r-sma candidate — the whole point of
|
||||
/// dropping `--strategy r-sma`/`--fast`/`--slow` for a positional blueprint +
|
||||
/// generic `--axis`. Every other generalize E2E test here still exercises
|
||||
/// `r_sma.json`, so a regression that silently special-cased r-sma paths
|
||||
/// back in (e.g. resolving axes against a hardcoded `sma_signal.*` namespace)
|
||||
/// would ship green there while breaking every other blueprint. This runs the
|
||||
/// unrelated r-breakout candidate (the ganged `channel_length` axis, no
|
||||
/// `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
|
||||
/// cleanly on a data refusal.
|
||||
/// Property (#220 generalize vertical, ported onto the campaign
|
||||
/// `std::generalize` stage by #319 Task 8): grades ANY sweepable blueprint,
|
||||
/// not just the historical r-sma candidate — originally the whole point of
|
||||
/// the retired `aura generalize` verb dropping `--strategy r-sma`/`--fast`/
|
||||
/// `--slow` for a positional blueprint + generic `--axis`; the property
|
||||
/// survives the #319 dissolution onto a `std::sweep, std::generalize`
|
||||
/// process document unchanged. Every other generalize E2E test here still
|
||||
/// exercises `r_sma.json`, so a regression that silently special-cased
|
||||
/// r-sma paths back in (e.g. resolving axes against a hardcoded
|
||||
/// `sma_signal.*` namespace) would ship green there while breaking every
|
||||
/// other blueprint. This runs the unrelated r-breakout candidate (the
|
||||
/// ganged `channel_length` axis, no `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 cleanly on a data refusal.
|
||||
#[test]
|
||||
fn generalize_grades_a_non_r_sma_blueprint() {
|
||||
// Ported onto a `[std::sweep, std::generalize]` campaign document
|
||||
@@ -2367,18 +2347,20 @@ fn generalize_grades_a_non_r_sma_blueprint() {
|
||||
}
|
||||
|
||||
/// Characterization pin (byte-identity anchor for the walkforward dissolution,
|
||||
/// #210). The current `aura walkforward` runs its per-window IS-refit inline
|
||||
/// (`walkforward_family` -> `select_winner` -> OOS run -> stitch/pool); the
|
||||
/// dissolution reroutes the same rolling IS-sweep -> OOS-run -> deflation through
|
||||
/// the campaign `std::walk_forward` stage, and must retain the OOS pip curve the
|
||||
/// campaign runner currently discards (`oos_equity: vec![]`) to reproduce
|
||||
/// #210, ported onto the campaign path by #319 Task 8). The original `aura
|
||||
/// walkforward` verb ran its per-window IS-refit inline (`walkforward_family`
|
||||
/// -> `select_winner` -> OOS run -> stitch/pool); the dissolution rerouted the
|
||||
/// same rolling IS-sweep -> OOS-run -> deflation through the campaign
|
||||
/// `std::walk_forward` stage, retaining the OOS pip curve the campaign runner
|
||||
/// used to discard (`oos_equity: vec![]`) so it could still reproduce
|
||||
/// `stitched_total_pips`, plus reconstruct pooled `oos_r`. The multi-point grid
|
||||
/// (fast 3,5 x slow 12,20) makes the per-window winner selection non-degenerate --
|
||||
/// `param_stability` varies across windows -- so a winner-pick divergence between
|
||||
/// the inline and campaign refit cannot ship green. This pins the EXACT current
|
||||
/// grade of the identical invocation over a fixed 2025 GER40 window; after the
|
||||
/// dissolution the same command must reproduce these bytes (the acceptance gate).
|
||||
/// Gated on the shared GER40 archive; skips cleanly on a data refusal.
|
||||
/// the inline and campaign refit could not have shipped green. This pins the
|
||||
/// EXACT grade of the identical invocation over a fixed 2025 GER40 window that
|
||||
/// the dissolved `std::walk_forward` stage reproduces bit-for-bit (the
|
||||
/// acceptance gate the port had to clear). Gated on the shared GER40 archive;
|
||||
/// skips cleanly on a data refusal.
|
||||
#[test]
|
||||
fn walkforward_real_e2e_pins_the_exact_current_grade() {
|
||||
// Ported onto a `[std::grid, std::walk_forward]` campaign document
|
||||
@@ -2453,21 +2435,24 @@ fn walkforward_real_e2e_pins_the_exact_current_grade() {
|
||||
assert_eq!(ps[1]["mean"].as_f64(), Some(17.333333333333332), "slow-MA refit mean: {wf}");
|
||||
}
|
||||
|
||||
/// Property (#220 walkforward vertical): `aura walkforward --real` IS-refits ANY
|
||||
/// sweepable blueprint through the campaign path, not just the historical r-sma
|
||||
/// candidate — the whole point of dropping `--strategy r-sma`/`--fast`/`--slow`
|
||||
/// for a positional blueprint + generic `--axis`. Every other walkforward E2E
|
||||
/// test here still exercises `r_sma.json`; a regression that silently
|
||||
/// special-cased the refit's axis lookup back to a hardcoded `sma_signal.*`
|
||||
/// namespace (e.g. in `walkforward_summary_json_from_reports`'s per-axis
|
||||
/// `raw_matches_wrapped` search) would ship green there while breaking every
|
||||
/// other blueprint. This runs the unrelated r-breakout candidate
|
||||
/// (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
|
||||
/// 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
|
||||
/// 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.
|
||||
/// Property (#220 walkforward vertical, ported onto the campaign
|
||||
/// `std::walk_forward` stage by #319 Task 8): IS-refits ANY sweepable
|
||||
/// blueprint, not just the historical r-sma candidate — originally the
|
||||
/// whole point of the retired `aura walkforward --real` verb dropping
|
||||
/// `--strategy r-sma`/`--fast`/`--slow` for a positional blueprint + generic
|
||||
/// `--axis`; the property survives the #319 dissolution unchanged. Every
|
||||
/// other walkforward E2E test here still exercises `r_sma.json`; a
|
||||
/// regression that silently special-cased the refit's axis lookup back to a
|
||||
/// hardcoded `sma_signal.*` namespace (e.g. in
|
||||
/// `walkforward_summary_json_from_reports`'s per-axis `raw_matches_wrapped`
|
||||
/// search) would ship green there while breaking every other blueprint.
|
||||
/// This runs the unrelated r-breakout candidate (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 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 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.
|
||||
#[test]
|
||||
fn walkforward_dissolves_a_non_r_sma_blueprint() {
|
||||
// Ported onto a `[std::grid, std::walk_forward]` campaign document
|
||||
@@ -2515,8 +2500,6 @@ fn walkforward_dissolves_a_non_r_sma_blueprint() {
|
||||
assert_eq!(ps.len(), 3, "one entry per bound axis (channel_length, stop_length, stop_k): {wf}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Characterization pin (#215): the same identical invocation as
|
||||
/// `walkforward_real_e2e_pins_the_exact_current_grade` (fixture, real
|
||||
/// instrument, axes, stop, window), but selecting `plateau:worst` instead of
|
||||
@@ -2597,17 +2580,6 @@ fn walkforward_real_e2e_pins_the_exact_current_plateau_grade() {
|
||||
assert_eq!(ps[1]["mean"].as_f64(), Some(16.444444444444443), "slow-MA refit mean: {wf}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Note (#319 Task 8, disposition finding): `generalize_without_explicit_
|
||||
// window_resolves_a_shared_window_and_completes` and `generalize_without_
|
||||
// explicit_window_resolves_the_intersection_of_all_symbols` retired rather
|
||||
@@ -2626,12 +2598,6 @@ fn walkforward_real_e2e_pins_the_exact_current_plateau_grade() {
|
||||
// window" property stays fully covered by `generalize_grades_a_candidate_
|
||||
// across_two_instruments` and its siblings above.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Property, ported onto a `[std::sweep, std::generalize]` campaign document
|
||||
/// (#319 Task 8 — disposition finding): a successful campaign generalize run
|
||||
/// persists its per-instrument runs as discoverable, but NOT as a merged
|
||||
@@ -2843,7 +2809,6 @@ fn aura_sweep_loads_a_blueprint_and_persists_a_sweep_family() {
|
||||
assert!(fo.contains("\"members\":4"), "2×2 grid -> four members: {fo}");
|
||||
}
|
||||
|
||||
|
||||
/// Property (#158/#164, C18/C11/C12): a blueprint sweep content-addresses its topology
|
||||
/// — the canonical blueprint is persisted ONCE under `runs/blueprints/<topology_hash>.json`,
|
||||
/// keyed by the very SHA256 every member's manifest carries, holding exactly the bytes
|
||||
@@ -3133,8 +3098,6 @@ fn aura_walkforward_over_a_blueprint_persists_a_family() {
|
||||
assert_eq!(n, 1, "exactly one content-addressed blueprint stored");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// E2E (#278 decision: same fault class, same verb, same surface => same
|
||||
/// treatment): a member fault on the SYNTHETIC walk-forward path is CONTAINED
|
||||
/// exactly like the real/campaign path contains it (#272) — it surfaces as an
|
||||
@@ -3218,14 +3181,6 @@ fn aura_sweep_synthetic_member_panic_is_contained() {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// E2E (negative): `aura reproduce <unknown>` is a hard error (exit non-zero + named
|
||||
/// cause on stderr), distinct from `runs family <id>`'s treat-as-empty exit 0.
|
||||
#[test]
|
||||
@@ -3275,13 +3230,6 @@ fn aura_reproduce_rejects_a_missing_stored_blueprint() {
|
||||
assert!(out.contains("missing from store"), "names the cause: {out}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// `--version` / `-V` surface the crate version on stdout and exit 0 (GNU MUST):
|
||||
/// a version query is a successful query, not a usage error. The expected string
|
||||
/// is the crate version itself (`CARGO_PKG_VERSION`, i.e. the workspace version),
|
||||
@@ -3335,13 +3283,6 @@ fn version_flag_carries_the_engine_commit_from_manifest_provenance() {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Characterization pin (byte-identity anchor for the mc R-bootstrap dissolution,
|
||||
/// #210, the last verb; argv since migrated to the generic form by #220).
|
||||
/// `aura mc <blueprint> --real --axis …` pools the per-window OOS trade-R
|
||||
@@ -3715,8 +3656,6 @@ fn mc_real_fits_the_injected_roller_to_a_short_window() {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Property: the one load-bearing decision new to the mc dissolution — `translate_mc`
|
||||
/// maps `campaign.seed` to the mc `--seed` (unlike `translate_walkforward`, which
|
||||
/// hardcodes `seed: 0`) — actually flows through the executor to the emitted grade,
|
||||
@@ -3962,15 +3901,6 @@ fn generalize_campaign_runs_an_arbitrary_blueprint() {
|
||||
assert!(grade["worst_case"].is_number(), "worst-case floor present: {record_line}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Property (#273, list reshape): `aura data list` enumerates the archive's
|
||||
/// symbols as NDJSON, sorted — the discovery step a campaign author (or an
|
||||
/// agent operating through the CLI alone, the deployment posture) needs
|
||||
|
||||
@@ -234,6 +234,30 @@ fn exec_campaign_target_outside_project_refuses() {
|
||||
assert!(!dir.join("runs").exists(), "a refused run must not create a store outside a project");
|
||||
}
|
||||
|
||||
/// A malformed FILE target (a trailing comma — not valid JSON at all) must
|
||||
/// refuse NEUTRALLY at the routing seam, before either leg's own
|
||||
/// document-shape validation ever runs — not silently fall through to the
|
||||
/// blueprint leg's "blueprint document is not valid JSON" prose (review
|
||||
/// Minor-3). The file's shape (blueprint vs. campaign) was never
|
||||
/// determined, so neither leg's own wording is the right attribution.
|
||||
#[test]
|
||||
fn exec_malformed_json_file_refuses_neutrally_not_misrouted() {
|
||||
let (dir, _fixture) = fresh_project();
|
||||
let malformed = r#"{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "broken",
|
||||
}"#;
|
||||
write_doc(&dir, "broken.campaign.json", malformed);
|
||||
let (out, code) = run_code_in(&dir, &["exec", "broken.campaign.json"]);
|
||||
assert_eq!(code, Some(2), "stdout/stderr: {out}");
|
||||
assert!(out.contains("target file is not valid JSON"), "stdout/stderr: {out}");
|
||||
assert!(
|
||||
!out.contains("blueprint document is not valid JSON"),
|
||||
"must not misattribute to the blueprint leg's own prose: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Task 2: the exec blueprint leg (synthetic single run).
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -448,7 +472,9 @@ fn exec_override_malformed_token_refuses_with_usage() {
|
||||
}
|
||||
|
||||
/// An override path naming no param of the blueprint (open or bound) refuses
|
||||
/// with `override_paths`' own prose (`member.rs:768-771`).
|
||||
/// with `override_paths`' own prose (`member.rs:768-771`), pointing at the
|
||||
/// live axis-discovery verb (`aura graph introspect --params`) — the retired
|
||||
/// `aura sweep --list-axes` no longer exists.
|
||||
#[test]
|
||||
fn exec_override_unknown_path_refuses_with_the_override_prose() {
|
||||
let (out, code) = run_code(&["exec", "examples/r_sma.json", "--override", "nope.knob=1"]);
|
||||
@@ -457,6 +483,34 @@ fn exec_override_unknown_path_refuses_with_the_override_prose() {
|
||||
out.contains("names no param of this blueprint (open or bound)"),
|
||||
"stdout/stderr: {out}"
|
||||
);
|
||||
assert!(
|
||||
out.contains("aura graph introspect --params"),
|
||||
"must point at the live discovery verb, not the retired --list-axes: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
/// A repeated `--override` path (the same NODE.PARAM named twice) refuses as
|
||||
/// a usage error naming the duplicated path — it must NOT fall through to
|
||||
/// `reopen_all`'s `NoSuchBoundParam` panic (the second reopen of an
|
||||
/// already-reopened bound param, exit 101): the first occurrence reopens
|
||||
/// `fast.length`, so a second `override_paths` resolution against the SAME
|
||||
/// un-reopened bound-name set still succeeds, and only `reopen_all`'s fold
|
||||
/// discovers the collision, too late to refuse cleanly. Mirrors `--tap`'s own
|
||||
/// duplicate refusal (`tap_plan_from_args`).
|
||||
#[test]
|
||||
fn exec_override_duplicate_path_refuses_with_usage_not_a_panic() {
|
||||
let (out, code) = run_code(&[
|
||||
"exec",
|
||||
"examples/r_sma.json",
|
||||
"--override",
|
||||
"fast.length=5",
|
||||
"--override",
|
||||
"fast.length=9",
|
||||
]);
|
||||
assert_ne!(code, Some(101), "must not panic: {out}");
|
||||
assert_eq!(code, Some(2), "stdout/stderr: {out}");
|
||||
assert!(out.contains("fast.length"), "the duplicated path must be named: {out}");
|
||||
assert!(out.contains("--override"), "stdout/stderr: {out}");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -535,6 +589,47 @@ fn exec_campaign_override_colliding_with_a_document_axis_refuses() {
|
||||
assert!(out.contains("declared axis"), "stdout/stderr: {out}");
|
||||
}
|
||||
|
||||
/// A repeated `--override` path on the campaign leg refuses at the SAME
|
||||
/// shared lexer (`parse_override_tokens`) both legs use — before the
|
||||
/// per-strategy injection loop (`run_campaign_returning`) ever runs. Prior
|
||||
/// to the fix, the first occurrence's own injection made the SECOND
|
||||
/// occurrence look like a document-declared axis, misattributing the
|
||||
/// refusal to "collides with a declared axis" (review Minor-1) even though
|
||||
/// `bias.scale` is not declared by the document at all.
|
||||
#[test]
|
||||
fn exec_campaign_override_duplicate_path_refuses_at_the_shared_lexer() {
|
||||
let (dir, _fixture) = fresh_project_with_data();
|
||||
let runs_dir = dir.join("runs");
|
||||
std::fs::remove_dir_all(&runs_dir).ok();
|
||||
let _cleanup = ScratchGuard(vec![
|
||||
ScratchPath::Dir(runs_dir.clone()),
|
||||
ScratchPath::File(dir.join("ovdup.process.json")),
|
||||
ScratchPath::File(dir.join("ovdup.campaign.json")),
|
||||
]);
|
||||
let bp_id = seed_blueprint(&dir, "exec-override-dup-seed");
|
||||
let proc_id = register_process_doc(&dir, "ovdup.process.json", SWEEP_ONLY_PROCESS_DOC);
|
||||
let doc = campaign_doc_json_for("SYMA", &bp_id, &proc_id, (1709251200000, 1719791999999));
|
||||
write_doc(&dir, "ovdup.campaign.json", &doc);
|
||||
|
||||
let (out, code) = run_code_in(
|
||||
&dir,
|
||||
&[
|
||||
"exec",
|
||||
"ovdup.campaign.json",
|
||||
"--override",
|
||||
"bias.scale=0.75",
|
||||
"--override",
|
||||
"bias.scale=0.9",
|
||||
],
|
||||
);
|
||||
assert_eq!(code, Some(2), "stdout/stderr: {out}");
|
||||
assert!(out.contains("bias.scale"), "the duplicated path must be named: {out}");
|
||||
assert!(
|
||||
!out.contains("collides with a declared axis"),
|
||||
"must not misattribute to the axis-collision cause: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
/// An override path naming no param of any strategy (a typo'd raw name)
|
||||
/// falls out of `validate_campaign_refs`'s existing `AxisNotInParamSpace`
|
||||
/// did-you-mean prose — pinned literally in `research_docs.rs` (:797-822);
|
||||
|
||||
Reference in New Issue
Block a user