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:
2026-07-09 10:01:33 +02:00
parent 3198747b48
commit 70a045cde9
+177
View File
@@ -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