test: red for #83 — Registry::load must read a legacy pre-0047 bare-float params line

RED test pinning the real defect of bug F5 (surfaced by the milestone
"The World, part II" fieldtest). Registry::load cannot deserialize a
runs.jsonl line written before the cycle-0047 typed-Scalar params
migration, where each param value is a bare JSON float (e.g.
["sma_cross.fast",2.0]) rather than the tagged {"F64":2.0}. The test
feeds a minimal autonomous legacy line and asserts it loads — the bare
float read back via the documented f64 coercion — instead of erroring at
the first param.

Fails RED at the diagnosed cause (Parse "expected value" on the bare
float). GREEN side (back-compat read, no change to the typed-Scalar
forward format) follows.

refs #83
This commit is contained in:
2026-06-17 16:09:36 +02:00
parent 562791e613
commit 20511d5d5c
+25
View File
@@ -236,6 +236,31 @@ mod tests {
let _ = fs::remove_file(&path); let _ = fs::remove_file(&path);
} }
/// A `runs.jsonl` line written before the cycle-0047 typed-`Scalar` params
/// migration carries each param value as a **bare JSON number** (`2.0`),
/// where the current format tags it (`{"F64":2.0}`). `load()` is the durable
/// C18 read-path and must stay back-compatible across that wire-shape change:
/// a legacy bare-float line must still load, the bare float read back as the
/// documented `f64` coercion — not erroring at the first param. (The current
/// typed shape is already covered by the round-trip test.)
#[test]
fn load_reads_a_legacy_bare_float_params_line() {
let path = temp_path("legacy_bare_float");
let _ = fs::remove_file(&path);
// An autonomous, minimal legacy line: a single bare-float param is the
// whole trigger (the rest of the RunReport is in the current shape). This
// mirrors the real pre-0047 store, where the first param `2.0` is where
// the typed-Scalar deserializer first chokes.
let legacy = r#"{"manifest":{"commit":"c","params":[["sma_cross.fast",2.0]],"window":[1,2],"seed":0,"broker":"b"},"metrics":{"total_pips":0.0,"max_drawdown":0.0,"exposure_sign_flips":0}}"#;
fs::write(&path, format!("{legacy}\n")).expect("write legacy line");
let reg = Registry::open(&path);
let reports = reg.load().expect("a legacy bare-float params line must still load");
assert_eq!(reports.len(), 1);
// the bare float reads back as the documented f64 coercion
assert_eq!(reports[0].manifest.params, vec![("sma_cross.fast".to_string(), Scalar::f64(2.0))]);
let _ = fs::remove_file(&path);
}
#[test] #[test]
fn rank_by_orders_best_first_per_metric() { fn rank_by_orders_best_first_per_metric() {
let reports = vec![ let reports = vec![