From abb6fbaa87e46571951cbcb2309b2bdcb833edb3 Mon Sep 17 00:00:00 2001 From: Brummel Date: Sat, 11 Jul 2026 14:27:28 +0200 Subject: [PATCH] =?UTF-8?q?test(cli):=20RED=20=E2=80=94=20chart=20cannot?= =?UTF-8?q?=20open=20a=20sweep=20--trace=20family=20by=20its=20printed=20h?= =?UTF-8?q?andle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Milestone-fieldtest finding B1: TraceStore::name_kind/read_family resolve taps exactly one directory below the handle, so the depth-2 per-member layout sweep/walkforward --trace writes (#224, ///index.json) classifies as NotFound and `aura chart ` exits 1 — with a remedy string that suggests re-running the very command that produced the data. The new autonomous test fabricates the depth-2 family via TraceStore and pins: chart on the printed family handle exits 0 and emits the HTML. refs milestone fieldtest 09da04f --- crates/aura-cli/tests/cli_run.rs | 82 ++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/crates/aura-cli/tests/cli_run.rs b/crates/aura-cli/tests/cli_run.rs index 98e2119..5bed84f 100644 --- a/crates/aura-cli/tests/cli_run.rs +++ b/crates/aura-cli/tests/cli_run.rs @@ -568,6 +568,88 @@ fn chart_unknown_name_refuses_with_exit_1_and_message() { let _ = std::fs::remove_dir_all(&cwd); } +/// Property (milestone fieldtest B1): a sweep / walk-forward `--trace` family is +/// chartable by the very handle the producing run prints. Those verbs fan a +/// selection-free sweep out per member into the #224 depth-2 layout +/// `runs/traces////index.json` (one directory level deeper +/// than the campaign path's depth-1 nominee layout `//index.json`). +/// `aura chart ` must resolve that fan-out as a family: exit 0 with the +/// chart page on stdout, covering the members' taps. AND the genuinely-absent- +/// handle refusal must not tell the user to re-run a command with that same +/// handle as the `--trace` argument — that command produced the data (it would +/// already be on disk), so pointing back at it is a dead-end remedy. +/// +/// The fixture is fabricated inline through `TraceStore` (no archive, no +/// data-server, no sweep run): two members under one cell, exactly the depth-2 +/// shape `persist_campaign_traces`' member fan-out writes. On today's tree the +/// depth-1-only `name_kind` / `read_family` classify the handle as NotFound, so +/// `chart` exits 1 with the misleading remedy — RED on both counts. +#[test] +fn chart_opens_a_sweep_trace_family_by_its_printed_handle() { + use aura_core::{Scalar, ScalarKind, Timestamp}; + use aura_engine::{ColumnarTrace, RunManifest}; + use aura_registry::TraceStore; + + let cwd = temp_cwd("chart-sweep-family"); + + let manifest = || RunManifest { + commit: "c0ffee".to_string(), + params: vec![("fast".to_string(), Scalar::f64(2.0))], + window: (Timestamp(1), Timestamp(2)), + seed: 0, + broker: "sim-optimal(pip_size=1)".to_string(), + selection: None, + instrument: Some("GER40".to_string()), + topology_hash: None, + project: None, + }; + let taps = |a: f64, b: f64| { + vec![ColumnarTrace::from_rows( + "equity", + &[ScalarKind::F64], + &[(Timestamp(1), vec![Scalar::f64(a)]), (Timestamp(2), vec![Scalar::f64(b)])], + )] + }; + + // The #224 sweep fan-out layout: two members under ONE cell, so each member's + // index.json lives at depth 2 (`///index.json`) — not the + // depth-1 nominee layout (`//index.json`) the campaign path writes. + // `TraceStore::open` roots at `/traces`, matching `env.trace_store()`. + let store = TraceStore::open(cwd.join("runs")); + store.write("swp/cell0/m0", &manifest(), &taps(0.0, 0.4)).expect("write member m0"); + store.write("swp/cell0/m1", &manifest(), &taps(0.0, 0.7)).expect("write member m1"); + + // (1) chart the family by the handle the sweep prints: exit 0 + chart page. + let out = Command::new(BIN).args(["chart", "swp"]).current_dir(&cwd).output().expect("spawn chart swp"); + assert_eq!( + out.status.code(), + Some(0), + "charting a sweep --trace family by its handle must exit 0; got {:?}, stderr: {}", + out.status.code(), + String::from_utf8_lossy(&out.stderr), + ); + let stdout = String::from_utf8_lossy(&out.stdout); + assert!( + stdout.contains("window.AURA_TRACES") && stdout.contains("aura chart"), + "the family handle must render a chart page covering its members' taps \ + (AURA_TRACES + chart title); stdout len {}", + stdout.len(), + ); + + // (2) the genuinely-absent-handle refusal must not suggest re-running a + // command with the handle itself as the `--trace` argument (a dead-end remedy). + let ghost = Command::new(BIN).args(["chart", "ghost"]).current_dir(&cwd).output().expect("spawn chart ghost"); + assert_eq!(ghost.status.code(), Some(1), "an absent handle still exits 1: {:?}", ghost.status); + let gerr = String::from_utf8_lossy(&ghost.stderr); + assert!( + !gerr.contains("--trace ghost"), + "the not-found remedy must not tell the user to re-run with the handle as --trace \ + (that command produced the data / would not create this handle); got: {gerr}", + ); + + let _ = std::fs::remove_dir_all(&cwd); +} + #[test] fn no_args_prints_usage_and_exits_two() { let out = Command::new(BIN).output().expect("spawn aura");