From c70b4cc2d2bba026591bdcbc482256f9aac113d9 Mon Sep 17 00:00:00 2001 From: Brummel Date: Sat, 11 Jul 2026 17:20:14 +0200 Subject: [PATCH] =?UTF-8?q?test(cli):=20RED=20=E2=80=94=20chart=20cannot?= =?UTF-8?q?=20resolve=20a=20--trace=20family=20by=20its=20chosen=20name=20?= =?UTF-8?q?(#238)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --trace lands the name in the generated campaign document, but no lookup surface consumes it: the trace store knows only the content-derived handle {campaign8}-{run}, so the user cannot address their own family by the name they chose. refs #238 --- crates/aura-cli/tests/cli_run.rs | 119 +++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/crates/aura-cli/tests/cli_run.rs b/crates/aura-cli/tests/cli_run.rs index 3b2553b..3e20dec 100644 --- a/crates/aura-cli/tests/cli_run.rs +++ b/crates/aura-cli/tests/cli_run.rs @@ -650,6 +650,125 @@ fn chart_opens_a_sweep_trace_family_by_its_printed_handle() { let _ = std::fs::remove_dir_all(&cwd); } +/// Property (#238): a sweep / walk-forward `--trace ` family is chartable by +/// the very NAME the user chose — not only by the content-derived handle the run +/// prints. `--trace ` lands `NAME` in the generated campaign document's +/// `name` field (content-addressed via `put_campaign`), while the trace directory +/// stays keyed by the content-derived handle `{campaign8}-{run}` that +/// `append_campaign_run` re-derives (C18, untouched). Today `aura chart ` +/// classifies `NAME` as an unknown trace handle and exits 1, so the chosen name +/// has no charting effect. It must instead resolve `NAME` against the recorded +/// campaign runs: the ONE record whose stored campaign document carries +/// `name == NAME` and a persisted `trace_name` charts that family by its handle — +/// exit 0 with the same chart page charting the handle directly renders. +/// +/// The state is fabricated in-process, no archive / sweep run: a campaign +/// document carrying a distinctive `name` is stored via `put_campaign`; a +/// campaign-run record with a `Some` trace_name sentinel is appended (which +/// re-derives the handle `{campaign8}-0`); a family is written under that exact +/// handle via `TraceStore` — mirroring the handle-charted sibling above (the +/// #224 depth-2 fan-out). The registry roots where `env.registry()` does +/// (`/runs/runs.jsonl`), so the running binary reads the same store. On +/// today's tree `chart ` hits `emit_chart`'s NotFound arm (no registry +/// query), so it exits 1 with the "no recorded run or family" message — RED for +/// the right reason (the name-resolution is absent, not a fabrication error). +#[test] +fn chart_resolves_a_trace_family_by_its_chosen_campaign_name() { + use aura_core::{Scalar, ScalarKind, Timestamp}; + use aura_engine::{ColumnarTrace, RunManifest}; + use aura_registry::{derive_trace_name, CampaignRunRecord, Registry, TraceStore}; + + let cwd = temp_cwd("chart-by-campaign-name"); + + // The chosen family name (`--trace `) — distinctive, not a trace handle + // (no such directory under runs/traces, so `name_kind` classifies NotFound). + const CAMPAIGN_NAME: &str = "mom-ger40-sept24"; + + // (1) Store a campaign document carrying that name (a valid CampaignDoc, so a + // resolver can parse it by any strategy — Value lookup or full deserialize). + // `put_campaign` self-keys by content, returning the id the campaign-run + // record must reference. The registry roots where `env.registry()` does: + // `/runs/runs.jsonl` (campaign docs at `/runs/campaigns/.json`, + // campaign_runs at `/runs/campaign_runs.jsonl`). + let reg = Registry::open(cwd.join("runs").join("runs.jsonl")); + let campaign_json = r#"{ + "format_version": 1, + "kind": "campaign", + "name": "__NAME__", + "data": { "instruments": ["GER40"], + "windows": [ { "from_ms": 1, "to_ms": 2 } ] }, + "strategies": [ + { "ref": { "content_id": "9f3a" }, + "axes": { "fast": { "kind": "I64", "values": [8, 12] } } } + ], + "process": { "ref": { "content_id": "4e2d" } }, + "seed": 1, + "presentation": { "persist_taps": ["equity"], "emit": ["family_table"] } + }"# + .replace("__NAME__", CAMPAIGN_NAME); + let campaign_id = reg.put_campaign(&campaign_json).expect("store campaign document"); + + // (2) Append a campaign-run record whose trace_name is Some (the executor's + // claim sentinel; `append_campaign_run` assigns the per-campaign run counter + // and re-derives the stored trace_name to `{campaign8}-{run}`). + let record = CampaignRunRecord { + campaign: campaign_id.clone(), + process: "4e2d".to_string(), + run: 0, // ignored — append assigns the counter + seed: 1, + cells: vec![], + generalizations: vec![], + trace_name: Some("claim".to_string()), + }; + let run = reg.append_campaign_run(&record).expect("append campaign run"); + let handle = derive_trace_name(&campaign_id, run); + + // (3) Write a family under that exact handle via TraceStore — the same + // fabrication the handle-charted sibling uses (#224 depth-2: two members under + // one cell). Charting `handle` directly is thus known to render a family chart. + 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)])], + )] + }; + let store = TraceStore::open(cwd.join("runs")); + store.write(&format!("{handle}/cell0/m0"), &manifest(), &taps(0.0, 0.4)).expect("write member m0"); + store.write(&format!("{handle}/cell0/m1"), &manifest(), &taps(0.0, 0.7)).expect("write member m1"); + + // Chart by the chosen NAME (not the handle): resolution must go through the + // campaign-run query and land on `handle`, rendering the same family chart. + let out = Command::new(BIN).args(["chart", CAMPAIGN_NAME]).current_dir(&cwd).output().expect("spawn chart "); + assert_eq!( + out.status.code(), + Some(0), + "charting a --trace family by its chosen campaign name 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 chosen name must render the same family chart as its handle \ + (AURA_TRACES + chart title); stdout len {}", + stdout.len(), + ); + + 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");