From f3f32b82698ef3da3cd5c9c89173db1a34eb4701 Mon Sep 17 00:00:00 2001 From: Brummel Date: Mon, 6 Jul 2026 15:49:46 +0200 Subject: [PATCH] =?UTF-8?q?test(cli):=20pin=20the=20exact=20generalize=20g?= =?UTF-8?q?rade=20=E2=80=94=20byte-identity=20anchor=20(#210)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current `aura generalize` binds its protective stop as a grid axis (`r_sma_sweep_family` with `stop_open=true`); the pending generalize dissolution rebinds the same stop through the risk-regime seam (`RiskRegime::Vol -> StopRule::Vol`). The existing generalize e2e (`generalize_grades_a_candidate_across_two_instruments`) asserts shape only — `worst_case` and the per-instrument R floats are never checked — so a divergence between the two stop bindings would ship green. This adds `generalize_real_e2e_pins_the_exact_current_grade`, capturing the exact current R grade of the identical invocation (GER40 0.0105637…, USDJPY 0.0057959…, worst_case 0.0057959…, sign_agreement 2). Green today against the inline path; after the dissolution the same command must reproduce these bytes — the acceptance gate that catches a stop-mechanism divergence loudly instead of silently. Resolves the generalize-dissolution grounding block (the unpinned stop-mechanism R equivalence) by the risk-regime characterization-pin pattern. refs #210 --- crates/aura-cli/tests/cli_run.rs | 55 ++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/crates/aura-cli/tests/cli_run.rs b/crates/aura-cli/tests/cli_run.rs index f87b2dd..8c7c677 100644 --- a/crates/aura-cli/tests/cli_run.rs +++ b/crates/aura-cli/tests/cli_run.rs @@ -3582,6 +3582,61 @@ fn generalize_grades_a_candidate_across_two_instruments() { assert!(stdout.contains("\"family_id\":\"generalize-0\""), "family handle: {stdout}"); } +/// Characterization pin (byte-identity anchor for the generalize dissolution, +/// #210). The current `aura generalize` binds its protective stop as a grid axis +/// (`r_sma_sweep_family` with `stop_open=true`); the dissolution rebinds the same +/// stop through the risk-regime seam (`RiskRegime::Vol -> StopRule::Vol`). The +/// sibling `generalize_grades_a_candidate_across_two_instruments` asserts shape +/// only — `worst_case` and the per-instrument R floats are never checked — so a +/// stop-mechanism divergence between the two bindings would ship green. This pins +/// the EXACT current R grade of the identical invocation; after the dissolution +/// the same command must reproduce these bytes (the acceptance gate), and any +/// drift fails here loudly. Gated on the shared GER40/USDJPY Sept-2024 archive; +/// skips cleanly on a data refusal. +#[test] +fn generalize_real_e2e_pins_the_exact_current_grade() { + const FROM_MS: &str = "1725148800000"; + const TO_MS: &str = "1727740799999"; + let cwd = temp_cwd("generalize-exact-grade"); + let out = std::process::Command::new(env!("CARGO_BIN_EXE_aura")) + .args([ + "generalize", "--strategy", "r-sma", "--real", "GER40,USDJPY", + "--fast", "3", "--slow", "12", "--stop-length", "14", "--stop-k", "2.0", + "--from", FROM_MS, "--to", TO_MS, + ]) + .current_dir(&cwd) + .output() + .expect("spawn aura"); + if out.status.code() == Some(1) { + let stderr = String::from_utf8_lossy(&out.stderr); + assert!( + stderr.contains("no local data") || stderr.contains("no recorded geometry"), + "exit 1 must be a data refusal, got: {stderr}" + ); + eprintln!("skip: no local GER40/USDJPY data"); + return; + } + assert_eq!(out.status.code(), Some(0), "exit: {:?}", out.status); + let stdout = String::from_utf8(out.stdout).expect("utf-8 stdout"); + let grade_line = stdout + .lines() + .find(|l| l.starts_with("{\"generalize\":")) + .unwrap_or_else(|| panic!("the generalize grade line: {stdout}")); + let v: serde_json::Value = serde_json::from_str(grade_line).expect("grade line parses as JSON"); + let g = &v["generalize"]; + assert_eq!(g["metric"].as_str(), Some("expectancy_r"), "metric: {grade_line}"); + assert_eq!(g["n_instruments"].as_u64(), Some(2), "two instruments: {grade_line}"); + assert_eq!(g["sign_agreement"].as_u64(), Some(2), "both agree in sign: {grade_line}"); + // EXACT R floats — the byte-identity anchor the dissolution must preserve. + assert_eq!(g["worst_case"].as_f64(), Some(0.005795903617609842), "worst-case R floor: {grade_line}"); + let per = g["per_instrument"].as_array().expect("per_instrument is an array"); + assert_eq!(per.len(), 2, "two per-instrument grades: {grade_line}"); + assert_eq!(per[0][0].as_str(), Some("GER40"), "first instrument: {grade_line}"); + assert_eq!(per[0][1].as_f64(), Some(0.01056371324510624), "GER40 expectancy R: {grade_line}"); + assert_eq!(per[1][0].as_str(), Some("USDJPY"), "second instrument: {grade_line}"); + assert_eq!(per[1][1].as_f64(), Some(0.005795903617609842), "USDJPY expectancy R: {grade_line}"); +} + /// Property: a single-instrument generalize is refused at parse time (exit 2), /// before any data access — so it asserts the arity refusal on any machine. #[test]