refactor(aura-runner, aura-cli): retire the family builders and the CLI topology_hash duplicate
Slice 7 of the #319 retirement. aura-runner's blueprint_sweep_family / blueprint_walkforward_family / blueprint_mc_family lost their only production callers with the quintet and are gone (family.rs 889 -> 199 lines) together with their zero-caller helpers; reproduce's live inputs (DataSource, showcase/walkforward sources) stay. Coverage was ported first: member-computation identity + family-shared topology_hash now pin through a campaign-document twin in tests/exec.rs, reproduce's multi-member and MC-seed-label branches re-mint their fixtures directly. The CLI-side topology_hash helper goes with them — its keep-rationale was disproven (the record line's hash is computed in the runner's member machinery; minuted on the issue) — and c28_layering's file list drops the stale verb_sugar entry. The synthetic per-seed MC family remains an intentional retirement casualty, recorded in the test dispositions. refs #319
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
//! exec — the one executor verb (#319): campaign file, campaign id,
|
||||
//! blueprint single run. Flag discipline is per-leg.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
mod common;
|
||||
@@ -681,6 +682,124 @@ fn exec_campaign_walkforward_with_trades_emits_no_zero_trade_note() {
|
||||
assert!(!out.contains("recorded zero trades"), "a traded run emits no zero-trade note: {out}");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Task 9: `aura-runner` family-builder retirement — the run-semantics half of
|
||||
// the retired `blueprint_sweep_family` unit test, ported onto the surviving
|
||||
// campaign path.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// [`campaign_doc_json_for`]'s shape with exactly ONE declared axis
|
||||
/// (`fast.length`) at caller-chosen values — `slow.length`/`bias.scale` stay
|
||||
/// at their bound defaults (4 and 0.5, `examples/r_sma.json`), isolating the
|
||||
/// swept dimension for a clean member-by-member comparison.
|
||||
fn campaign_doc_json_one_axis(
|
||||
instrument: &str,
|
||||
bp_id: &str,
|
||||
proc_id: &str,
|
||||
window: (i64, i64),
|
||||
fast_values: &str,
|
||||
) -> String {
|
||||
format!(
|
||||
r#"{{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "member-parity",
|
||||
"data": {{ "instruments": ["{instrument}"], "windows": [ {{ "from_ms": {from}, "to_ms": {to} }} ] }},
|
||||
"strategies": [ {{ "ref": {{ "content_id": "{bp}" }},
|
||||
"axes": {{ "fast.length": {{ "kind": "I64", "values": [{fast_values}] }} }} }} ],
|
||||
"process": {{ "ref": {{ "content_id": "{proc}" }} }},
|
||||
"seed": 7,
|
||||
"presentation": {{ "persist_taps": [], "emit": ["family_table"] }}
|
||||
}}"#,
|
||||
from = window.0,
|
||||
to = window.1,
|
||||
bp = bp_id,
|
||||
proc = proc_id,
|
||||
)
|
||||
}
|
||||
|
||||
/// Property, ported from the retired `blueprint_sweep_family` unit test
|
||||
/// `blueprint_sweep_member_equals_single_run_and_shares_topology_hash` (#319,
|
||||
/// the family builders retired): a campaign family member computes the
|
||||
/// IDENTICAL trading result a STANDALONE one-cell campaign at that same point
|
||||
/// computes — the sweep terminal reuses the same member-run path regardless
|
||||
/// of how many sibling points ride along — and every member of one family
|
||||
/// carries the SAME `topology_hash` (the loaded blueprint's, not a per-point
|
||||
/// value). `exec`'s blueprint leg has no `--real` (invariant 7: "a real-data
|
||||
/// single run is a one-cell campaign document"), so the "single run"
|
||||
/// comparator here IS the one-cell campaign the retirement itself designates
|
||||
/// as that replacement, run over the identical instrument+window as the
|
||||
/// multi-member family.
|
||||
#[test]
|
||||
fn exec_campaign_member_equals_a_standalone_single_cell_and_shares_topology_hash() {
|
||||
let (dir, _fixture) = fresh_project_with_data();
|
||||
let runs_dir = dir.join("runs");
|
||||
std::fs::remove_dir_all(&runs_dir).ok();
|
||||
let _cleanup = ScratchGuard(vec![
|
||||
ScratchPath::Dir(runs_dir.clone()),
|
||||
ScratchPath::File(dir.join("parity.process.json")),
|
||||
ScratchPath::File(dir.join("parity_family.campaign.json")),
|
||||
ScratchPath::File(dir.join("parity_single.campaign.json")),
|
||||
]);
|
||||
let bp_id = seed_blueprint(&dir, "exec-parity-seed");
|
||||
let proc_id = register_process_doc(&dir, "parity.process.json", SWEEP_ONLY_PROCESS_DOC);
|
||||
let window = (1709251200000, 1719791999999);
|
||||
|
||||
// (A) a 2-member family: fast.length in {2, 8}; slow.length/bias.scale
|
||||
// stay at their bound defaults.
|
||||
let doc_family = campaign_doc_json_one_axis("SYMA", &bp_id, &proc_id, window, "2, 8");
|
||||
write_doc(&dir, "parity_family.campaign.json", &doc_family);
|
||||
let (out_family, code_family) = run_code_in(&dir, &["exec", "parity_family.campaign.json"]);
|
||||
assert_eq!(code_family, Some(0), "family run: {out_family}");
|
||||
let family_members: Vec<serde_json::Value> = out_family
|
||||
.lines()
|
||||
.filter(|l| l.starts_with(r#"{"family_id":"#))
|
||||
.map(|l| serde_json::from_str(l).expect("member line parses"))
|
||||
.collect();
|
||||
assert_eq!(family_members.len(), 2, "a 2-value axis yields 2 members: {out_family}");
|
||||
let topo_values: HashSet<&str> = family_members
|
||||
.iter()
|
||||
.map(|m| m["report"]["manifest"]["topology_hash"].as_str().expect("topology_hash present"))
|
||||
.collect();
|
||||
assert_eq!(
|
||||
topo_values.len(),
|
||||
1,
|
||||
"every member of one family shares ONE topology_hash: {family_members:?}"
|
||||
);
|
||||
let member_8 = family_members
|
||||
.iter()
|
||||
.find(|m| {
|
||||
m["report"]["manifest"]["params"]
|
||||
.as_array()
|
||||
.expect("params array")
|
||||
.iter()
|
||||
.any(|e| e[0] == "fast.length" && e[1] == serde_json::json!({"I64": 8}))
|
||||
})
|
||||
.expect("the fast.length=8 member is present");
|
||||
|
||||
// (B) a standalone one-cell campaign pinned at the SAME point.
|
||||
let doc_single = campaign_doc_json_one_axis("SYMA", &bp_id, &proc_id, window, "8");
|
||||
write_doc(&dir, "parity_single.campaign.json", &doc_single);
|
||||
let (out_single, code_single) = run_code_in(&dir, &["exec", "parity_single.campaign.json"]);
|
||||
assert_eq!(code_single, Some(0), "single-cell run: {out_single}");
|
||||
let single_member: serde_json::Value = out_single
|
||||
.lines()
|
||||
.find(|l| l.starts_with(r#"{"family_id":"#))
|
||||
.map(|l| serde_json::from_str(l).expect("member line parses"))
|
||||
.expect("the one-cell campaign's single member line");
|
||||
|
||||
assert_eq!(
|
||||
member_8["report"]["metrics"], single_member["report"]["metrics"],
|
||||
"a family member computes the identical result a standalone single-cell run at \
|
||||
the same point computes"
|
||||
);
|
||||
assert_eq!(
|
||||
member_8["report"]["manifest"]["topology_hash"],
|
||||
single_member["report"]["manifest"]["topology_hash"],
|
||||
"the standalone single-cell run shares the same topology_hash"
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Task 8: `cli_run.rs` disposition — ports onto `exec` (retire+port half only;
|
||||
// the port-to-campaign-document half is a separate follow-up dispatch).
|
||||
|
||||
Reference in New Issue
Block a user