test: red for the false C1 refusal on non-default regime trace persist
Two RED tests pinning the lossy record boundary: CellRealization carries no regime, so persist_campaign_traces' drift-alarm re-run hardcodes the default vol-stop and false-fails C1 for any campaign with a non-default risk regime + persist taps (facet A), and campaign_cell_key has no regime discriminator so two regimes' traces would collide in one dir (facet B, masked by A's refusal today). refs #219, refs #212
This commit is contained in:
@@ -1897,6 +1897,183 @@ fn campaign_run_real_e2e_two_regimes_each_stamp_their_own_stop() {
|
||||
expect_for("-r1-", 8, 1.2);
|
||||
}
|
||||
|
||||
/// RED (#219 / #212): the campaign trace-persist re-run must reproduce the
|
||||
/// nominee it recorded, INCLUDING when the cell ran under a non-default risk
|
||||
/// regime. `run_blueprint_member` stamps the regime-resolved stop into the
|
||||
/// member manifest (`stop_length`/`stop_k`), but `persist_campaign_traces`
|
||||
/// rebuilds the re-run's `point` only from the SIGNAL blueprint's param space
|
||||
/// (the SMA lengths) and hardcodes the DEFAULT `Vol { R_SMA_STOP_LENGTH,
|
||||
/// R_SMA_STOP_K }` (3, 2.0) for the stop (campaign_run.rs). For a non-default
|
||||
/// regime the re-run's stop differs from the recorded run's stop, its metrics
|
||||
/// diverge, and the C1 drift alarm returns a FALSE "trace re-run diverged from
|
||||
/// the recorded nominee (C1 violation)" refusal — exit 1 on a run that is in
|
||||
/// fact perfectly deterministic. The record boundary (`CellRealization`) drops
|
||||
/// the regime; the fix threads it through so the re-run binds the cell's OWN
|
||||
/// stop. Every other persist e2e in this file is regime-less (default risk),
|
||||
/// which is exactly why the bug shipped unnoticed. Gated on the local GER40
|
||||
/// archive like its siblings; skips on a data-less host.
|
||||
#[test]
|
||||
fn campaign_persist_non_default_regime_does_not_false_fail_c1() {
|
||||
let _fixture = project_lock();
|
||||
let dir = built_project();
|
||||
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("regime-persist.process.json")),
|
||||
ScratchPath::File(dir.join("regime-persist.campaign.json")),
|
||||
]);
|
||||
let bp_id = seed_blueprint(dir, "campaign-persist-regime-seed");
|
||||
let proc_id =
|
||||
register_process_doc(dir, "regime-persist.process.json", SWEEP_ONLY_PROCESS_DOC);
|
||||
let base = campaign_doc_json(
|
||||
&bp_id,
|
||||
&proc_id,
|
||||
(1725148800000, 1727740799999),
|
||||
"\"equity\", \"r_equity\"",
|
||||
"\"family_table\"",
|
||||
);
|
||||
// A non-default regime: length != 3 or k != 2.0. Vol { 5, 3.0 } is a 50%
|
||||
// wider stop over a longer ATR window — its exits, R rows, and metrics
|
||||
// differ from the default the persist re-run hardcodes.
|
||||
let with_regime = base.replacen(
|
||||
"\"seed\": 7,",
|
||||
"\"seed\": 7,\n \"risk\": [ { \"vol\": { \"length\": 5, \"k\": 3.0 } } ],",
|
||||
1,
|
||||
);
|
||||
assert_ne!(with_regime, base, "replacen must actually match the fixture's seed field");
|
||||
write_doc(dir, "regime-persist.campaign.json", &with_regime);
|
||||
let (out, code) = run_code_in(dir, &["campaign", "run", "regime-persist.campaign.json"]);
|
||||
|
||||
// Skip on a data-less machine: the member-data refusal, never a panic.
|
||||
if code == Some(1)
|
||||
&& (out.contains("no recorded geometry") || out.contains("no data for instrument"))
|
||||
{
|
||||
eprintln!("skip: no local GER40 data for the campaign regime-persist e2e");
|
||||
return;
|
||||
}
|
||||
|
||||
// The symptom, pinned exactly: the persist re-run hardcodes the default
|
||||
// stop and false-fails the C1 drift alarm for the non-default regime.
|
||||
assert!(
|
||||
!out.contains("trace re-run diverged from the recorded nominee (C1 violation)"),
|
||||
"the persist re-run must bind the cell's own regime stop, not false-fail C1: {out}"
|
||||
);
|
||||
assert_eq!(
|
||||
code,
|
||||
Some(0),
|
||||
"a deterministic non-default-regime campaign persists cleanly: {out}"
|
||||
);
|
||||
|
||||
// The nominee's taps actually landed (not merely that the refusal was
|
||||
// suppressed): traces/<name>/<cell_key>/equity.json exists.
|
||||
let record_line = out
|
||||
.lines()
|
||||
.find(|l| l.starts_with("{\"campaign_run\":"))
|
||||
.expect("the always-on final campaign_run line");
|
||||
let v: serde_json::Value =
|
||||
serde_json::from_str(record_line).expect("campaign_run line parses as JSON");
|
||||
let trace_name = v["campaign_run"]["trace_name"]
|
||||
.as_str()
|
||||
.expect("persist_taps non-empty => the record carries trace_name");
|
||||
let cell_key = format!("{}-GER40-w0", &bp_id[..8]);
|
||||
let cell_dir = runs_dir.join("traces").join(trace_name).join(&cell_key);
|
||||
assert!(
|
||||
cell_dir.join("equity.json").is_file(),
|
||||
"the non-default-regime nominee's equity tap persists at {}",
|
||||
cell_dir.display()
|
||||
);
|
||||
}
|
||||
|
||||
/// RED (#212 — the trace-dir collision facet of the same #219 record gap): two
|
||||
/// risk regimes over the SAME (strategy, instrument, window) cell must persist
|
||||
/// their traces into DISTINCT directories. `campaign_cell_key` names dirs
|
||||
/// `strategy8-instrument-w{window_ordinal}` with NO regime discriminator
|
||||
/// (campaign_run.rs), so both regimes' nominees resolve to the identical
|
||||
/// `strategy8-GER40-w0` member dir and the second write clobbers the first.
|
||||
/// NOTE (masking): today this fails EARLIER — at the same false C1 refusal as
|
||||
/// its sibling above, because the persist re-run's hardcoded default stop
|
||||
/// diverges from each non-default regime's recorded nominee. Once the regime is
|
||||
/// threaded so the re-run stops false-failing, THIS assertion (two distinct
|
||||
/// on-disk trace dirs, one per regime) becomes the live guard that the cell key
|
||||
/// gained its `-r{ordinal}` discriminator — without it the two regimes collide
|
||||
/// into one dir. Gated on the local GER40 archive; skips on a data-less host.
|
||||
#[test]
|
||||
fn campaign_persist_two_regimes_land_in_distinct_trace_dirs() {
|
||||
let _fixture = project_lock();
|
||||
let dir = built_project();
|
||||
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("tworegimes-persist.process.json")),
|
||||
ScratchPath::File(dir.join("tworegimes-persist.campaign.json")),
|
||||
]);
|
||||
let bp_id = seed_blueprint(dir, "campaign-persist-two-regimes-seed");
|
||||
let proc_id = register_process_doc(
|
||||
dir,
|
||||
"tworegimes-persist.process.json",
|
||||
SWEEP_ONLY_PROCESS_DOC,
|
||||
);
|
||||
let base = campaign_doc_json(
|
||||
&bp_id,
|
||||
&proc_id,
|
||||
(1725148800000, 1727740799999),
|
||||
"\"equity\"",
|
||||
"\"family_table\"",
|
||||
);
|
||||
let with_regimes = base.replacen(
|
||||
"\"seed\": 7,",
|
||||
"\"seed\": 7,\n \"risk\": [ { \"vol\": { \"length\": 5, \"k\": 3.0 } }, \
|
||||
{ \"vol\": { \"length\": 8, \"k\": 1.2 } } ],",
|
||||
1,
|
||||
);
|
||||
assert_ne!(with_regimes, base, "replacen must actually match the fixture's seed field");
|
||||
write_doc(dir, "tworegimes-persist.campaign.json", &with_regimes);
|
||||
let (out, code) = run_code_in(dir, &["campaign", "run", "tworegimes-persist.campaign.json"]);
|
||||
|
||||
// Skip on a data-less machine: the member-data refusal, never a panic.
|
||||
if code == Some(1)
|
||||
&& (out.contains("no recorded geometry") || out.contains("no data for instrument"))
|
||||
{
|
||||
eprintln!("skip: no local GER40 data for the two-regime persist e2e");
|
||||
return;
|
||||
}
|
||||
|
||||
// Precondition (facet A): neither regime's persist re-run may false-fail C1.
|
||||
assert!(
|
||||
!out.contains("trace re-run diverged from the recorded nominee (C1 violation)"),
|
||||
"the persist re-run must bind each cell's own regime stop, not false-fail C1: {out}"
|
||||
);
|
||||
assert_eq!(code, Some(0), "a two-regime campaign persists both cells cleanly: {out}");
|
||||
|
||||
// The guard (facet B): each regime's equity tap lands in its OWN dir under
|
||||
// traces/<name>/, so there are TWO distinct tap-bearing dirs, not one.
|
||||
let record_line = out
|
||||
.lines()
|
||||
.find(|l| l.starts_with("{\"campaign_run\":"))
|
||||
.expect("the always-on final campaign_run line");
|
||||
let v: serde_json::Value =
|
||||
serde_json::from_str(record_line).expect("campaign_run line parses as JSON");
|
||||
let trace_name = v["campaign_run"]["trace_name"]
|
||||
.as_str()
|
||||
.expect("persist_taps non-empty => the record carries trace_name");
|
||||
let family_dir = runs_dir.join("traces").join(trace_name);
|
||||
let mut tap_dirs: Vec<String> = std::fs::read_dir(&family_dir)
|
||||
.expect("trace family dir exists")
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| e.path().join("equity.json").is_file())
|
||||
.map(|e| e.file_name().to_string_lossy().into_owned())
|
||||
.collect();
|
||||
tap_dirs.sort();
|
||||
tap_dirs.dedup();
|
||||
assert_eq!(
|
||||
tap_dirs.len(),
|
||||
2,
|
||||
"two regimes over one cell persist into two distinct trace dirs, got {tap_dirs:?}"
|
||||
);
|
||||
}
|
||||
|
||||
/// Property: an UNPRODUCIBLE tap requested alongside a producible one degrades
|
||||
/// gracefully, never a crash or a silently-dropped whole request. `net_r_equity`
|
||||
/// is in the closed tap vocabulary (`validate_campaign` accepts it) but has no
|
||||
|
||||
Reference in New Issue
Block a user