diff --git a/crates/aura-cli/tests/research_docs.rs b/crates/aura-cli/tests/research_docs.rs index 36bdc80..ad5b1e7 100644 --- a/crates/aura-cli/tests/research_docs.rs +++ b/crates/aura-cli/tests/research_docs.rs @@ -667,6 +667,58 @@ fn campaign_introspect_unwired_lists_the_risk_slot_when_explicitly_empty() { ); } +/// #265: the cost and risk slots name their knobs but not their UNITS or +/// SEMANTICS, and the two natural misreadings are silent (wrong by orders of +/// magnitude / change an experiment's meaning, with no error). Property: when +/// `campaign introspect --unwired` lists the open cost and risk slots, each of +/// the four silently-misreadable knobs carries a one-line unit/semantics note +/// that blocks its misreading — `constant.cost_per_trade` is in PRICE units +/// (charged per close as cost/|entry−stop| in R), `vol_slippage.slip_vol_mult` +/// is a MULTIPLIER on the per-cycle vol estimate (charged in R likewise), +/// `vol.length` SMOOTHS the estimator and does NOT set the stop's timescale, +/// and `vol.k` SCALES the stop distance. Autonomous: a bare `{}` envelope +/// (both slots open) written inline, no project, hostless. Wording stays free +/// (matched case-insensitively on load-bearing fragments, not whole lines); it +/// fails today because the annotations are absent, not on scaffolding. +#[test] +fn campaign_introspect_unwired_annotates_cost_and_risk_knob_units() { + let dir = temp_cwd("campaign-introspect-unwired-knob-units"); + write_doc(&dir, "bare.json", "{}"); + let (out, code) = run_code_in(&dir, &["campaign", "introspect", "--unwired", "bare.json"]); + assert_eq!(code, Some(0), "stdout/stderr: {out}"); + let low = out.to_lowercase(); + + // constant.cost_per_trade — price units, not R (the order-of-magnitude trap). + assert!(low.contains("cost_per_trade"), "the cost knob must still be named: {out}"); + assert!( + low.contains("price units"), + "cost_per_trade must be annotated as a PRICE-unit numerator (its misreading is a cost in R): {out}" + ); + + // vol_slippage.slip_vol_mult — a multiplier on the per-cycle vol estimate. + assert!(low.contains("slip_vol_mult"), "the vol-slippage knob must still be named: {out}"); + assert!( + low.contains("multiplier on the per-cycle vol"), + "slip_vol_mult must be annotated as a MULTIPLIER on the per-cycle vol estimate: {out}" + ); + + // vol.length — smooths the estimator; it does NOT set the stop's timescale. + assert!( + low.contains("smooths"), + "vol.length must be annotated as SMOOTHING the per-cycle vol estimator: {out}" + ); + assert!( + low.contains("does not set the stop's timescale"), + "vol.length must state it does NOT set the stop's timescale (its exact misreading): {out}" + ); + + // vol.k — scales the stop distance (the risk-unit lever). + assert!( + low.contains("scales the stop distance"), + "vol.k must be annotated as SCALING the stop distance (the risk-unit lever): {out}" + ); +} + #[test] fn campaign_introspect_unwired_reports_the_spec_example_slots() { let dir = temp_cwd("campaign-introspect-unwired");