feat(0092): run-from-blueprint infrastructure — restructure + topology_hash + shared seam (Tasks 1-3)
Cycle-1 infrastructure for #165 (World/C21). Behaviour-preserving + workspace green; the CLI arm + tests (Tasks 4-5) follow. Task 1 — restructured stage1_r_graph into stage1_signal() (the serializable signal leg: SMA-cross -> Bias, a `price` input-role + a `bias` output) + wrap_stage1r(signal, ...) (broker/sinks/exec/cost around a nested signal), with stage1_r_graph() = wrap_stage1r(stage1_signal(...)). DEVIATION from the plan's "callers untouched" claim, verified behaviour-preserving: nesting the signal prefixes its param-space names (stage1_signal.fast.length), which broke the sweep's bare .axis("fast.length"); fixed exactly as the #137 stop-knob machinery does — FAST_LENGTH_SUFFIX/SLOW_LENGTH_SUFFIX suffix-resolution + stage1_r_friendly_name arms mapping the prefixed names back to the bare manifest names. All observable behaviour (manifest names, member keys, metrics, traces) byte-identical; the flat graph and every metric unchanged (full suite green, incl. sweep/walkforward/MC). Task 2 — RunManifest.topology_hash: Option<String> (Tier-1 optional, serde default/skip — old manifests byte-identical, #156), threaded None into all 24 literal sites across 7 crates. Also fixed the aura-registry RunManifestRead read-mirror to carry topology_hash (it was hardcoding None on load) — else a stored hash would silently drop on the registry round-trip once Task 3 stores a real one. Task 3 — the shared run_signal_stage1r seam (hash the signal, wrap, compile with params, bootstrap, run, manifest with params from param_space + topology_hash) + the sha256_hex topology_hash helper (sha2 in aura-cli, off the frozen engine, invariant 8). run_stage1_r now carries its topology_hash too (every run becomes self-identifying, #158). The seam is unwired until Task 4 (transient #[allow(dead_code)], retired there); RED-first seam tests added. Verified: cargo test --workspace green (51 suites, 0 failures); cargo clippy --workspace --all-targets -D warnings clean. refs #165
This commit is contained in:
@@ -1002,6 +1002,7 @@ mod tests {
|
||||
broker: "test".to_string(),
|
||||
selection: None,
|
||||
instrument: None,
|
||||
topology_hash: None,
|
||||
},
|
||||
metrics: summarize(&equity, &exposure),
|
||||
}
|
||||
|
||||
@@ -244,6 +244,7 @@ mod tests {
|
||||
broker: "test".to_string(),
|
||||
selection: None,
|
||||
instrument: None,
|
||||
topology_hash: None,
|
||||
},
|
||||
metrics: summarize(&equity, &exposure),
|
||||
}
|
||||
@@ -269,6 +270,7 @@ mod tests {
|
||||
broker: "t".to_string(),
|
||||
selection: None,
|
||||
instrument: None,
|
||||
topology_hash: None,
|
||||
},
|
||||
metrics: RunMetrics {
|
||||
total_pips: v,
|
||||
|
||||
@@ -51,6 +51,12 @@ pub struct RunManifest {
|
||||
/// one-directional serde widening as `selection` (C14/C18).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub instrument: Option<String>,
|
||||
/// SHA256 (hex) of the canonical `blueprint_to_json` of the run's signal
|
||||
/// topology — the #158 reproducibility anchor. `None` for a run not built
|
||||
/// from a serialized blueprint and for every pre-0092 line. One-directional
|
||||
/// serde widening (Tier-1, #156), identical idiom to `selection` / `instrument`.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub topology_hash: Option<String>,
|
||||
}
|
||||
|
||||
/// A run's full structured result: the descriptor plus the metrics it
|
||||
@@ -273,6 +279,7 @@ mod tests {
|
||||
let m = RunManifest {
|
||||
commit: "c".into(), params: vec![], window: (Timestamp(0), Timestamp(0)),
|
||||
seed: 0, broker: "b".into(), selection: None, instrument: None,
|
||||
topology_hash: None,
|
||||
};
|
||||
assert!(!serde_json::to_string(&m).unwrap().contains("selection"));
|
||||
}
|
||||
@@ -290,6 +297,7 @@ mod tests {
|
||||
let with = RunManifest {
|
||||
commit: "c".into(), params: vec![], window: (Timestamp(0), Timestamp(0)),
|
||||
seed: 0, broker: "b".into(), selection: None, instrument: Some("GER40".into()),
|
||||
topology_hash: None,
|
||||
};
|
||||
let json = serde_json::to_string(&with).unwrap();
|
||||
assert!(json.contains("\"instrument\":\"GER40\""), "json: {json}");
|
||||
@@ -299,6 +307,7 @@ mod tests {
|
||||
let without = RunManifest {
|
||||
commit: "c".into(), params: vec![], window: (Timestamp(0), Timestamp(0)),
|
||||
seed: 0, broker: "b".into(), selection: None, instrument: None,
|
||||
topology_hash: None,
|
||||
};
|
||||
// skip_serializing_if omits the key when None -> existing manifest bytes unchanged (C23).
|
||||
assert!(!serde_json::to_string(&without).unwrap().contains("instrument"));
|
||||
@@ -316,6 +325,7 @@ mod tests {
|
||||
let m = RunManifest {
|
||||
commit: "c".into(), params: vec![], window: (Timestamp(0), Timestamp(0)),
|
||||
seed: 0, broker: "b".into(), selection: Some(sel.clone()), instrument: None,
|
||||
topology_hash: None,
|
||||
};
|
||||
let back: RunManifest = serde_json::from_str(&serde_json::to_string(&m).unwrap()).unwrap();
|
||||
assert_eq!(back.selection, Some(sel));
|
||||
@@ -455,6 +465,7 @@ mod tests {
|
||||
broker: "sim-optimal(pip_size=0.0001)".to_string(),
|
||||
selection: None,
|
||||
instrument: None,
|
||||
topology_hash: None,
|
||||
},
|
||||
metrics,
|
||||
}
|
||||
@@ -559,6 +570,7 @@ mod tests {
|
||||
broker: "sim-optimal(pip_size=1.0)".to_string(),
|
||||
selection: None,
|
||||
instrument: None,
|
||||
topology_hash: None,
|
||||
},
|
||||
metrics: RunMetrics {
|
||||
total_pips: 12.0,
|
||||
@@ -589,6 +601,7 @@ mod tests {
|
||||
broker: "sim-optimal(pip_size=1.0)".to_string(),
|
||||
selection: None,
|
||||
instrument: None,
|
||||
topology_hash: None,
|
||||
},
|
||||
metrics: RunMetrics { total_pips: 12.0, max_drawdown: 1.0, bias_sign_flips: 1, r: None },
|
||||
};
|
||||
@@ -611,6 +624,7 @@ mod tests {
|
||||
broker: "sim-optimal(pip_size=1.0)".to_string(),
|
||||
selection: None,
|
||||
instrument: None,
|
||||
topology_hash: None,
|
||||
},
|
||||
metrics: RunMetrics { total_pips: 12.0, max_drawdown: 1.0, bias_sign_flips: 1, r: None },
|
||||
};
|
||||
|
||||
@@ -669,6 +669,7 @@ mod tests {
|
||||
broker: "test".to_string(),
|
||||
selection: None,
|
||||
instrument: None,
|
||||
topology_hash: None,
|
||||
},
|
||||
metrics: summarize(&equity, &exposure),
|
||||
}
|
||||
|
||||
@@ -293,6 +293,7 @@ mod tests {
|
||||
broker: "t".to_string(),
|
||||
selection: None,
|
||||
instrument: None,
|
||||
topology_hash: None,
|
||||
},
|
||||
metrics: summarize(&[], &[]),
|
||||
}
|
||||
|
||||
@@ -117,6 +117,7 @@ fn run_point(point: &[Cell]) -> RunReport {
|
||||
broker: "test".to_string(),
|
||||
selection: None,
|
||||
instrument: None,
|
||||
topology_hash: None,
|
||||
},
|
||||
metrics: summarize(&equity, &exposure),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user