diff --git a/crates/aura-campaign/src/exec.rs b/crates/aura-campaign/src/exec.rs index b14956f..6949450 100644 --- a/crates/aura-campaign/src/exec.rs +++ b/crates/aura-campaign/src/exec.rs @@ -554,6 +554,7 @@ fn placeholder_report(params: &[(String, Scalar)], window_ms: (i64, i64)) -> Run manifest: RunManifest { commit: String::new(), params: params.to_vec(), + defaults: Vec::new(), window: (Timestamp(window_ms.0), Timestamp(window_ms.1)), seed: 0, broker: "faulted-member-placeholder".to_string(), diff --git a/crates/aura-campaign/src/lib.rs b/crates/aura-campaign/src/lib.rs index 4bd8bf4..0907e87 100644 --- a/crates/aura-campaign/src/lib.rs +++ b/crates/aura-campaign/src/lib.rs @@ -339,6 +339,7 @@ mod tests { manifest: RunManifest { commit: "test".to_string(), params: vec![], + defaults: vec![], window: (Timestamp(0), Timestamp(1)), seed: 0, broker: "sim".to_string(), @@ -1019,6 +1020,7 @@ mod wf_tests { manifest: RunManifest { commit: "wf-fake".to_string(), params: params.to_vec(), + defaults: Vec::new(), window: (Timestamp(window_ms.0), Timestamp(window_ms.1)), seed: 0, broker: "fake".to_string(), diff --git a/crates/aura-campaign/tests/execute.rs b/crates/aura-campaign/tests/execute.rs index eb7585d..abab2d6 100644 --- a/crates/aura-campaign/tests/execute.rs +++ b/crates/aura-campaign/tests/execute.rs @@ -58,6 +58,7 @@ fn planted_report(cell: &CellSpec, params: &[(String, Scalar)], window_ms: (i64, manifest: RunManifest { commit: "fake".to_string(), params: params.to_vec(), + defaults: Vec::new(), window: (Timestamp(window_ms.0), Timestamp(window_ms.1)), seed: 0, broker: "fake".to_string(), diff --git a/crates/aura-campaign/tests/member_seam_e2e.rs b/crates/aura-campaign/tests/member_seam_e2e.rs index 82e7308..8f724a2 100644 --- a/crates/aura-campaign/tests/member_seam_e2e.rs +++ b/crates/aura-campaign/tests/member_seam_e2e.rs @@ -46,6 +46,7 @@ impl MemberRunner for FixedRunner { manifest: RunManifest { commit: "e2e".to_string(), params: vec![], + defaults: vec![], window: (Timestamp(window_ms.0), Timestamp(window_ms.1)), seed: 0, broker: "test".to_string(), @@ -98,6 +99,7 @@ fn rankable_metrics_are_all_per_member_resolvable() { manifest: RunManifest { commit: "e2e".to_string(), params: vec![], + defaults: vec![], window: (Timestamp(0), Timestamp(1)), seed: 0, broker: "test".to_string(), diff --git a/crates/aura-cli/src/campaign_run.rs b/crates/aura-cli/src/campaign_run.rs index 77929e1..c50942c 100644 --- a/crates/aura-cli/src/campaign_run.rs +++ b/crates/aura-cli/src/campaign_run.rs @@ -1058,6 +1058,7 @@ mod tests { manifest: RunManifest { commit: "test".to_string(), params, + defaults: Vec::new(), window: (Timestamp(0), Timestamp(0)), seed: 0, broker: "test".to_string(), diff --git a/crates/aura-cli/src/main.rs b/crates/aura-cli/src/main.rs index 7de8078..78085d7 100644 --- a/crates/aura-cli/src/main.rs +++ b/crates/aura-cli/src/main.rs @@ -127,6 +127,7 @@ fn sim_optimal_manifest( RunManifest { commit: option_env!("AURA_COMMIT").unwrap_or("unknown").to_string(), params, + defaults: Vec::new(), window, seed, broker: format!("sim-optimal(pip_size={pip_size})"), @@ -137,6 +138,24 @@ fn sim_optimal_manifest( } } +/// The wrap-prefixed BOUND-param defaults of `signal` (#249): every +/// `bound_param_space()` entry as a `(., value)` pair, in +/// `bound_param_space()` order — the `RunManifest.defaults` field. Must be read +/// off `signal` BEFORE it is consumed by `wrap_r`/reopening: an axis-reopened +/// bound param has already left `bound_param_space()` by construction +/// (`Composite::reopen` forgets the bound value), so a caller that reopens +/// overrides before calling this naturally excludes them — no separate filter +/// needed. Same prefixing rule as `wrapped_bound_names`, kept separate because +/// that helper discards the value this one needs. +fn wrapped_bound_defaults(signal: &Composite) -> Vec<(String, Scalar)> { + let prefix = format!("{}.", signal.name()); + signal + .bound_param_space() + .into_iter() + .map(|b| (format!("{prefix}{}", b.name), b.value)) + .collect() +} + /// Map any byte outside the portable directory-name charset `[A-Za-z0-9._-]` to /// `_`. The single source of filesystem-portability for an on-disk path component /// (valid on Linux / Windows / macOS, also URL-path- and cloud-sync-safe). Used by @@ -1691,6 +1710,7 @@ fn run_signal_r( .iter() .map(|p| p.name.clone()) .collect(); + let defaults = wrapped_bound_defaults(&signal); // read before `signal` is consumed below let (tx_eq, rx_eq) = mpsc::channel(); let (tx_ex, rx_ex) = mpsc::channel(); let (tx_r, rx_r) = mpsc::channel(); @@ -1710,6 +1730,7 @@ fn run_signal_r( let named_params: Vec<(String, Scalar)> = names.into_iter().zip(params.iter().copied()).collect(); let mut manifest = sim_optimal_manifest(named_params, window, seed, pip_size); + manifest.defaults = defaults; manifest.broker = r_sma_broker_label(pip_size); manifest.topology_hash = Some(topo); manifest.project = env.provenance(); @@ -1739,6 +1760,7 @@ fn run_blueprint_member( binding: &binding::ResolvedBinding, cost: &[aura_research::CostSpec], ) -> RunReport { + let defaults = wrapped_bound_defaults(&signal); // read before `signal` is consumed below let (tx_eq, rx_eq) = mpsc::channel(); let (tx_ex, rx_ex) = mpsc::channel(); let (tx_r, rx_r) = mpsc::channel(); @@ -1779,6 +1801,7 @@ fn run_blueprint_member( named.push((format!("cost[{k}].{knob}"), Scalar::f64(v))); } let mut manifest = sim_optimal_manifest(named, window, seed, pip); + manifest.defaults = defaults; manifest.broker = r_sma_broker_label(pip); manifest.topology_hash = Some(topo.to_string()); manifest.project = env.provenance(); @@ -5497,6 +5520,38 @@ mod tests { assert!(!err.contains("UnknownKnob"), "must not leak the debug render: {err}"); } + /// #249: an axis-reopened bound param flows through `params` ("what + /// varied") and must NOT also appear in `defaults` ("what was held") — the + /// two are disjoint by construction. Sweeping `fast.length` on the fully + /// bound r_sma example leaves `slow.length`/`bias.scale` untouched (they + /// stay in `defaults`), while `fast.length` moves to `params` and drops + /// out of `defaults` entirely. + #[test] + fn sweep_override_excludes_the_reopened_default_from_the_manifest() { + let env = project::Env::std(); + let closed = load_closed_r_sma(); + let doc = blueprint_to_json(&closed).expect("serializes"); + let axes = vec![("sma_signal.fast.length".to_string(), vec![Scalar::i64(2)])]; + let fam = blueprint_sweep_family(&doc, &axes, &DataSource::Synthetic, &env) + .expect("a bound param is a default — the axis overrides it"); + assert_eq!(fam.points.len(), 1); + let manifest = &fam.points[0].report.manifest; + + let param_names: Vec<&str> = manifest.params.iter().map(|(n, _)| n.as_str()).collect(); + assert!(param_names.contains(&"sma_signal.fast.length"), "swept axis is a param: {param_names:?}"); + + let default_names: Vec<&str> = manifest.defaults.iter().map(|(n, _)| n.as_str()).collect(); + assert!( + !default_names.contains(&"sma_signal.fast.length"), + "the reopened default must not also appear in defaults: {default_names:?}" + ); + assert_eq!( + default_names, + ["sma_signal.slow.length", "sma_signal.bias.scale"], + "the untouched bound params stay in defaults: {default_names:?}" + ); + } + /// Property: an MC family's `aura reproduce` lines carry the member's own `seed=` /// label. MC members hold no tuning params (the params-join is empty), so the line would /// otherwise print a BLANK member label; the seed is each draw's realization identity and diff --git a/crates/aura-cli/tests/cli_run.rs b/crates/aura-cli/tests/cli_run.rs index c1dfd95..034fa02 100644 --- a/crates/aura-cli/tests/cli_run.rs +++ b/crates/aura-cli/tests/cli_run.rs @@ -603,6 +603,7 @@ fn chart_opens_a_sweep_trace_family_by_its_printed_handle() { let manifest = || RunManifest { commit: "c0ffee".to_string(), params: vec![("fast".to_string(), Scalar::f64(2.0))], + defaults: vec![], window: (Timestamp(1), Timestamp(2)), seed: 0, broker: "sim-optimal(pip_size=1)".to_string(), @@ -737,6 +738,7 @@ fn chart_resolves_a_trace_family_by_its_chosen_campaign_name() { let manifest = || RunManifest { commit: "c0ffee".to_string(), params: vec![("fast".to_string(), Scalar::f64(2.0))], + defaults: vec![], window: (Timestamp(1), Timestamp(2)), seed: 0, broker: "sim-optimal(pip_size=1)".to_string(), @@ -819,6 +821,7 @@ fn chart_by_campaign_name_refuses_when_the_name_is_ambiguous() { let manifest = || RunManifest { commit: "c0ffee".to_string(), params: vec![("fast".to_string(), Scalar::f64(2.0))], + defaults: vec![], window: (Timestamp(1), Timestamp(2)), seed: 0, broker: "sim-optimal(pip_size=1)".to_string(), diff --git a/crates/aura-engine/src/blueprint.rs b/crates/aura-engine/src/blueprint.rs index a7cd5a7..40eedd7 100644 --- a/crates/aura-engine/src/blueprint.rs +++ b/crates/aura-engine/src/blueprint.rs @@ -1763,6 +1763,7 @@ mod tests { manifest: RunManifest { commit: "test".to_string(), params: Vec::new(), + defaults: Vec::new(), window: (Timestamp(0), Timestamp(0)), seed: 0, broker: "test".to_string(), diff --git a/crates/aura-engine/src/mc.rs b/crates/aura-engine/src/mc.rs index 0dfacaf..730dd6b 100644 --- a/crates/aura-engine/src/mc.rs +++ b/crates/aura-engine/src/mc.rs @@ -239,6 +239,7 @@ mod tests { manifest: RunManifest { commit: "test".to_string(), params: Vec::new(), + defaults: Vec::new(), window, seed, broker: "test".to_string(), @@ -266,6 +267,7 @@ mod tests { manifest: RunManifest { commit: "t".to_string(), params: Vec::new(), + defaults: Vec::new(), window: (Timestamp(0), Timestamp(0)), seed: i as u64, broker: "t".to_string(), diff --git a/crates/aura-engine/src/report.rs b/crates/aura-engine/src/report.rs index b8a6f2d..ffc4ed9 100644 --- a/crates/aura-engine/src/report.rs +++ b/crates/aura-engine/src/report.rs @@ -34,6 +34,16 @@ pub struct RunManifest { /// self-describing [`Scalar`], so the param's kind (an `i64` length vs an /// `f64` scale) survives into the record instead of collapsing to `f64`. pub params: Vec<(String, Scalar)>, + /// The bound params this run did NOT vary — the wrap-prefixed + /// `bound_param_space()` of the signal, EXCLUDING any bound param an axis + /// reopened (those flow through `params` instead, #246). Complements + /// `params` ("what varied") with "what was held": the two are disjoint by + /// construction. Empty for every pre-#249 record. One-directional serde + /// widening (C14/C18), identical idiom to `selection`/`instrument`/ + /// `topology_hash` — except a `Vec`, not an `Option`, so it always + /// serializes (mirroring `params`), never skipped when empty. + #[serde(default)] + pub defaults: Vec<(String, Scalar)>, /// The data-window: inclusive `(from, to)` epoch-ns bounds (C12). pub window: (Timestamp, Timestamp), /// The RNG seed (C12 seed-as-input). `0` for a seed-free synthetic run. @@ -298,7 +308,7 @@ mod tests { #[test] fn runmanifest_without_selection_serialises_without_the_key() { let m = RunManifest { - commit: "c".into(), params: vec![], window: (Timestamp(0), Timestamp(0)), + commit: "c".into(), params: vec![], defaults: vec![], window: (Timestamp(0), Timestamp(0)), seed: 0, broker: "b".into(), selection: None, instrument: None, topology_hash: None, project: None, @@ -317,7 +327,7 @@ mod tests { #[test] fn runmanifest_instrument_round_trips_and_omits_when_none() { let with = RunManifest { - commit: "c".into(), params: vec![], window: (Timestamp(0), Timestamp(0)), + commit: "c".into(), params: vec![], defaults: vec![], window: (Timestamp(0), Timestamp(0)), seed: 0, broker: "b".into(), selection: None, instrument: Some("GER40".into()), topology_hash: None, project: None, @@ -328,7 +338,7 @@ mod tests { assert_eq!(back.instrument, Some("GER40".to_string())); let without = RunManifest { - commit: "c".into(), params: vec![], window: (Timestamp(0), Timestamp(0)), + commit: "c".into(), params: vec![], defaults: vec![], window: (Timestamp(0), Timestamp(0)), seed: 0, broker: "b".into(), selection: None, instrument: None, topology_hash: None, project: None, @@ -370,7 +380,7 @@ mod tests { commit: Some("deadbeef".into()), }; let m = RunManifest { - commit: "c".into(), params: vec![], window: (Timestamp(0), Timestamp(0)), + commit: "c".into(), params: vec![], defaults: vec![], window: (Timestamp(0), Timestamp(0)), seed: 0, broker: "b".into(), selection: None, instrument: None, topology_hash: None, project: Some(p.clone()), @@ -393,7 +403,7 @@ mod tests { neighbourhood_score: None, n_neighbours: None, }; let m = RunManifest { - commit: "c".into(), params: vec![], window: (Timestamp(0), Timestamp(0)), + commit: "c".into(), params: vec![], defaults: vec![], window: (Timestamp(0), Timestamp(0)), seed: 0, broker: "b".into(), selection: Some(sel.clone()), instrument: None, topology_hash: None, project: None, @@ -531,6 +541,7 @@ mod tests { ("sma_slow".to_string(), Scalar::i64(4)), ("bias_scale".to_string(), Scalar::f64(0.5)), ], + defaults: vec![], window: (Timestamp(1), Timestamp(5)), seed: 0, broker: "sim-optimal(pip_size=0.0001)".to_string(), @@ -637,6 +648,7 @@ mod tests { ("sma_slow".to_string(), Scalar::i64(4)), ("bias_scale".to_string(), Scalar::f64(1.0)), ], + defaults: vec![], window: (Timestamp(1), Timestamp(6)), seed: 0, broker: "sim-optimal(pip_size=1.0)".to_string(), @@ -654,7 +666,7 @@ mod tests { }; assert_eq!( report.to_json(), - r#"{"manifest":{"commit":"abc123","params":[["sma_fast",{"I64":2}],["sma_slow",{"I64":4}],["bias_scale",{"F64":1.0}]],"window":[1,6],"seed":0,"broker":"sim-optimal(pip_size=1.0)"},"metrics":{"total_pips":12.0,"max_drawdown":1.0,"bias_sign_flips":1}}"#, + r#"{"manifest":{"commit":"abc123","params":[["sma_fast",{"I64":2}],["sma_slow",{"I64":4}],["bias_scale",{"F64":1.0}]],"defaults":[],"window":[1,6],"seed":0,"broker":"sim-optimal(pip_size=1.0)"},"metrics":{"total_pips":12.0,"max_drawdown":1.0,"bias_sign_flips":1}}"#, ); } @@ -669,6 +681,7 @@ mod tests { ("sma_slow".to_string(), Scalar::i64(4)), ("bias_scale".to_string(), Scalar::f64(1.0)), ], + defaults: vec![], window: (Timestamp(1), Timestamp(6)), seed: 0, broker: "sim-optimal(pip_size=1.0)".to_string(), @@ -693,6 +706,7 @@ mod tests { ("sma_slow".to_string(), Scalar::i64(4)), ("bias_scale".to_string(), Scalar::f64(1.0)), ], + defaults: vec![], window: (Timestamp(1), Timestamp(6)), seed: 0, broker: "sim-optimal(pip_size=1.0)".to_string(), diff --git a/crates/aura-engine/src/sweep.rs b/crates/aura-engine/src/sweep.rs index 0a0c150..c348e1e 100644 --- a/crates/aura-engine/src/sweep.rs +++ b/crates/aura-engine/src/sweep.rs @@ -722,6 +722,7 @@ mod tests { manifest: RunManifest { commit: "test".to_string(), params: Vec::new(), + defaults: Vec::new(), window: (Timestamp(0), Timestamp(0)), seed: 0, broker: "test".to_string(), @@ -877,6 +878,7 @@ mod tests { manifest: RunManifest { commit: point.iter().map(|c| c.i64().to_string()).collect::>().join(","), params: Vec::new(), + defaults: Vec::new(), window: (Timestamp(0), Timestamp(0)), seed: 0, broker: "test".to_string(), diff --git a/crates/aura-engine/src/walkforward.rs b/crates/aura-engine/src/walkforward.rs index 7ca992c..baae39e 100644 --- a/crates/aura-engine/src/walkforward.rs +++ b/crates/aura-engine/src/walkforward.rs @@ -288,6 +288,7 @@ mod tests { manifest: RunManifest { commit: "t".to_string(), params: Vec::new(), + defaults: Vec::new(), window: (Timestamp(0), Timestamp(0)), seed: 0, broker: "t".to_string(), diff --git a/crates/aura-engine/tests/list_sweep_e2e.rs b/crates/aura-engine/tests/list_sweep_e2e.rs index e57810a..4b342e5 100644 --- a/crates/aura-engine/tests/list_sweep_e2e.rs +++ b/crates/aura-engine/tests/list_sweep_e2e.rs @@ -99,6 +99,7 @@ fn run_point(point: &[Cell]) -> RunReport { manifest: RunManifest { commit: "list-sweep-e2e".to_string(), params: Vec::new(), + defaults: Vec::new(), window: (Timestamp(0), Timestamp(0)), seed: 0, broker: "test".to_string(), diff --git a/crates/aura-engine/tests/random_sweep_e2e.rs b/crates/aura-engine/tests/random_sweep_e2e.rs index 190b804..a4f5222 100644 --- a/crates/aura-engine/tests/random_sweep_e2e.rs +++ b/crates/aura-engine/tests/random_sweep_e2e.rs @@ -112,6 +112,7 @@ fn run_point(point: &[Cell]) -> RunReport { manifest: RunManifest { commit: "random-sweep-e2e".to_string(), params: Vec::new(), + defaults: Vec::new(), window: (Timestamp(0), Timestamp(0)), seed: 0, broker: "test".to_string(), diff --git a/crates/aura-ingest/examples/ger40_breakout_sweep.rs b/crates/aura-ingest/examples/ger40_breakout_sweep.rs index 43d634d..99a31d5 100644 --- a/crates/aura-ingest/examples/ger40_breakout_sweep.rs +++ b/crates/aura-ingest/examples/ger40_breakout_sweep.rs @@ -63,6 +63,7 @@ fn run_point(server: &Arc, point: &[Cell], from: Timestamp, to: Time manifest: RunManifest { commit: "ger40-breakout-sweep".to_string(), params: vec![], + defaults: vec![], window: (from, to), seed: 0, broker: "sim-optimal(pip_size=1)".to_string(), diff --git a/crates/aura-ingest/examples/ger40_breakout_walkforward.rs b/crates/aura-ingest/examples/ger40_breakout_walkforward.rs index 916c66d..3160ebc 100644 --- a/crates/aura-ingest/examples/ger40_breakout_walkforward.rs +++ b/crates/aura-ingest/examples/ger40_breakout_walkforward.rs @@ -73,6 +73,7 @@ fn run_point( manifest: RunManifest { commit: "ger40-breakout-wfo".to_string(), params: vec![], + defaults: vec![], window: (from, to), seed: 0, broker: "sim-optimal(pip_size=1)".to_string(), diff --git a/crates/aura-ingest/examples/shared/breakout_real.rs b/crates/aura-ingest/examples/shared/breakout_real.rs index 26f167d..46e546c 100644 --- a/crates/aura-ingest/examples/shared/breakout_real.rs +++ b/crates/aura-ingest/examples/shared/breakout_real.rs @@ -396,6 +396,7 @@ pub fn report_from_trace(trace: &[BarTrace], from: Timestamp, to: Timestamp) -> ("entry_bar".to_string(), Scalar::i64(3)), ("exit_bar".to_string(), Scalar::i64(5)), ], + defaults: vec![], window: (from, to), seed: 0, broker: "sim-optimal(pip_size=1)".to_string(), diff --git a/crates/aura-ingest/tests/ger40_breakout_world.rs b/crates/aura-ingest/tests/ger40_breakout_world.rs index fb6284f..651f017 100644 --- a/crates/aura-ingest/tests/ger40_breakout_world.rs +++ b/crates/aura-ingest/tests/ger40_breakout_world.rs @@ -65,6 +65,7 @@ fn run_point(server: &Arc, point: &[Cell], from: Timestamp, to: Time manifest: RunManifest { commit: "ger40-breakout-world".to_string(), params: vec![], + defaults: vec![], window: (from, to), seed: 0, broker: "sim-optimal(pip_size=1)".to_string(), diff --git a/crates/aura-ingest/tests/real_bars.rs b/crates/aura-ingest/tests/real_bars.rs index 9b057b7..2f36c58 100644 --- a/crates/aura-ingest/tests/real_bars.rs +++ b/crates/aura-ingest/tests/real_bars.rs @@ -86,6 +86,7 @@ fn run_sample_over(prices: Vec<(Timestamp, Scalar)>) -> RunReport { ("sma_slow".to_string(), Scalar::i64(4)), ("bias_scale".to_string(), Scalar::f64(0.5)), ], + defaults: vec![], window, seed: 0, broker: "sim-optimal(pip_size=0.0001)".to_string(), diff --git a/crates/aura-ingest/tests/streaming_seam.rs b/crates/aura-ingest/tests/streaming_seam.rs index a8b3083..2ddaefa 100644 --- a/crates/aura-ingest/tests/streaming_seam.rs +++ b/crates/aura-ingest/tests/streaming_seam.rs @@ -86,6 +86,7 @@ fn run_sample(window: (Timestamp, Timestamp), source: Box) -> RunRep ("sma_slow".to_string(), Scalar::i64(4)), ("bias_scale".to_string(), Scalar::f64(0.5)), ], + defaults: vec![], window, seed: 0, broker: "sim-optimal(pip_size=0.0001)".to_string(), diff --git a/crates/aura-registry/src/compat.rs b/crates/aura-registry/src/compat.rs index c630609..8244033 100644 --- a/crates/aura-registry/src/compat.rs +++ b/crates/aura-registry/src/compat.rs @@ -29,6 +29,8 @@ pub(crate) struct RunReportRead { struct RunManifestRead { commit: String, params: Vec<(String, ScalarRead)>, + #[serde(default)] + defaults: Vec<(String, ScalarRead)>, window: (Timestamp, Timestamp), seed: u64, broker: String, @@ -73,12 +75,14 @@ impl<'de> Deserialize<'de> for ScalarRead { impl From for RunReport { fn from(r: RunReportRead) -> Self { let RunManifestRead { - commit, params, window, seed, broker, selection, instrument, topology_hash, project, + commit, params, defaults, window, seed, broker, selection, instrument, topology_hash, + project, } = r.manifest; RunReport { manifest: RunManifest { commit, params: params.into_iter().map(|(name, v)| (name, v.0)).collect(), + defaults: defaults.into_iter().map(|(name, v)| (name, v.0)).collect(), window, seed, broker, diff --git a/crates/aura-registry/src/lib.rs b/crates/aura-registry/src/lib.rs index 7493f30..22048d8 100644 --- a/crates/aura-registry/src/lib.rs +++ b/crates/aura-registry/src/lib.rs @@ -816,6 +816,7 @@ mod tests { manifest: RunManifest { commit: "c".to_string(), params: vec![("p".to_string(), Scalar::f64(1.0))], + defaults: vec![], window: (Timestamp(1), Timestamp(2)), seed: 0, broker: "b".to_string(), @@ -1182,7 +1183,7 @@ mod tests { params: vec![], report: RunReport { manifest: RunManifest { - commit: "c".into(), params: vec![], window: (Timestamp(0), Timestamp(0)), + commit: "c".into(), params: vec![], defaults: vec![], window: (Timestamp(0), Timestamp(0)), seed: 0, broker: "b".into(), selection: None, instrument: None, topology_hash: None, project: None, diff --git a/crates/aura-registry/src/trace_store.rs b/crates/aura-registry/src/trace_store.rs index 2c35a20..4bade17 100644 --- a/crates/aura-registry/src/trace_store.rs +++ b/crates/aura-registry/src/trace_store.rs @@ -299,6 +299,7 @@ mod tests { RunManifest { commit: "c".to_string(), params: vec![("p".to_string(), Scalar::f64(1.0))], + defaults: vec![], window: (Timestamp(1), Timestamp(5)), seed: 0, broker: "sim-optimal(pip_size=0.0001)".to_string(), diff --git a/crates/aura-registry/tests/campaign_run_store_e2e.rs b/crates/aura-registry/tests/campaign_run_store_e2e.rs index 02f2476..1cf01a8 100644 --- a/crates/aura-registry/tests/campaign_run_store_e2e.rs +++ b/crates/aura-registry/tests/campaign_run_store_e2e.rs @@ -30,6 +30,7 @@ fn tiny_report() -> RunReport { manifest: RunManifest { commit: "c".to_string(), params: vec![], + defaults: vec![], window: (Timestamp(1), Timestamp(2)), seed: 0, broker: "b".to_string(), diff --git a/docs/design/INDEX.md b/docs/design/INDEX.md index 082d371..7d2581a 100644 --- a/docs/design/INDEX.md +++ b/docs/design/INDEX.md @@ -1211,6 +1211,18 @@ every hand-rolled usage line reads `Usage: aura …` (#179, cycle 0101); refusal diagnostics stay unprefixed (diagnostics are not usage lines). The machine-first help surface (JSON/manifest help, stdin op-scripts) stays on the #157/C21 track, not this cycle (settled as human/GNU convention compliance). +**Amendment (2026-07-13, #249 — two artifact classes, two redundancy budgets).** +The data layer the programmatic face emits is a public interface read raw (by +humans and LLMs), and its records split into two classes with opposite +redundancy budgets: **generated outcome records** (manifests, metrics, family +reports) have a single writer at run time — redundancy there cannot drift and +is deliberately spent on direct readability; **authored intent artifacts** +(blueprints, op-scripts, campaign documents) are author-maintained — every +redundancy is a drift site and stays out. Consequence shipped with #249: a run +manifest stamps the untouched bound defaults (`defaults`, one-directionally +widened beside `params`, which keeps its "what varied" reproduce semantics) so +a raw reader of a fully-bound run no longer sees a misleading `"params": []`; +the blueprint itself carries no such duplication. ### C15 — Resampling-as-node; sessions/calendars **Guarantee.** A resampler is a node (finer stream → coarser bar stream),