feat(cli): annotator-stage seam flips + doc tidy + gated e2e with the mc suffix (0108 task 5)

The retired v1-boundary test is replaced by v2 pins: mc-before-wf
refuses with the executor shape prose (while process validate accepts
the same doc — the tier boundary observable across two commands, added
by the task-2 e2e phase and kept), single-instrument generalize
refuses data-free on the built fixture project, and the two
addressing-mode tests' stale rationale comments now name the
member-data seam they actually hit. PROCESS_BLOCKS doc strings drop
'in v1' (introspection text only — no content id moves). The gated
real-data e2e extends its process with the monte_carlo suffix and
asserts the pooled_oos bootstrap in the final campaign_run line — ran
its full assert path on this host.

Task-5 duplicates the plan had double-drafted (task 2's e2e phase
pre-landed three of its tests) were correctly NOT re-added.

Gates: workspace 1025/0 (fully green again), clippy -D warnings clean.

refs #200
This commit is contained in:
2026-07-04 01:28:06 +02:00
parent a1cd7bc6f5
commit 7e4e9a8827
2 changed files with 279 additions and 81 deletions
+277 -79
View File
@@ -644,8 +644,9 @@ fn seed_blueprint(dir: &Path, name: &str) -> String {
}
/// Register `doc` as a process document in the project store; returns its id.
/// Asserts register exits 0 — an intrinsically valid document (an mc-bearing
/// one included) must always register; only `campaign run` draws the v1 line.
/// Asserts register exits 0 — an intrinsically valid document (annotator
/// stages in any order included) must always register; only `campaign run`'s
/// preflight draws the executable-shape line.
fn register_process_doc(dir: &Path, file: &str, doc: &str) -> String {
write_doc(dir, file, doc);
let (out, code) = run_code_in(dir, &["process", "register", file]);
@@ -690,8 +691,10 @@ fn campaign_doc_json(
)
}
/// An mc-bearing process: intrinsically VALID (register accepts it) but past
/// the executable v1 boundary (`campaign run` refuses it at preflight).
/// An mc-bearing process: intrinsically valid AND (since the v2 executor) an
/// executable pipeline shape — `sweep -> monte_carlo` annotates each sweep
/// survivor. The two addressing-mode tests below run it over the [1, 2] 1970
/// window, so they refuse deterministically at the member-data seam.
const MC_PROCESS_DOC: &str = r#"{
"format_version": 1,
"kind": "process",
@@ -702,7 +705,38 @@ const MC_PROCESS_DOC: &str = r#"{
]
}"#;
/// The minimal executable v1 pipeline (one sweep stage).
/// Intrinsically valid but NOT an executable v2 shape: the annotator sits
/// before the population stage (`walk_forward` after `monte_carlo`). The
/// intrinsic tier deliberately permits this order (the shipped
/// `validate_process_permits_walk_forward_after_an_annotator` pin in
/// aura-research), so register accepts it; only `campaign run`'s preflight
/// refuses it.
const MC_BEFORE_WF_PROCESS_DOC: &str = r#"{
"format_version": 1,
"kind": "process",
"name": "mc-before-wf",
"pipeline": [
{ "block": "std::sweep", "metric": "sqn_normalized", "select": "argmax" },
{ "block": "std::monte_carlo", "resamples": 100, "block_len": 5 },
{ "block": "std::walk_forward", "in_sample_ms": 1209600000, "out_of_sample_ms": 604800000,
"step_ms": 604800000, "mode": "rolling", "metric": "sqn_normalized", "select": "argmax" }
]
}"#;
/// An executable generalize-bearing shape (`sweep -> generalize`). The
/// refusal under test is the campaign-side static guard (std::generalize
/// needs >= 2 instruments), not the pipeline shape.
const GENERALIZE_PROCESS_DOC: &str = r#"{
"format_version": 1,
"kind": "process",
"name": "gen-screen",
"pipeline": [
{ "block": "std::sweep", "metric": "sqn_normalized", "select": "argmax" },
{ "block": "std::generalize", "metric": "net_expectancy_r" }
]
}"#;
/// The minimal executable pipeline (one sweep stage).
const SWEEP_ONLY_PROCESS_DOC: &str = r#"{
"format_version": 1,
"kind": "process",
@@ -710,10 +744,14 @@ const SWEEP_ONLY_PROCESS_DOC: &str = r#"{
"pipeline": [ { "block": "std::sweep", "metric": "sqn_normalized", "select": "argmax" } ]
}"#;
/// The full v1 shape for the gated e2e: sweep -> gate -> walk_forward. The
/// gate (`n_trades ge 0`) passes every member, so walk-forward always has
/// survivors. Roller: 14d IS / 7d OOS / 7d step in epoch-ms, tiling the
/// ~30-day GER40 Sept-2024 campaign window.
/// The full annotated shape for the gated e2e: sweep -> gate -> walk_forward
/// -> monte_carlo. The gate (`n_trades ge 0`) passes every member, so
/// walk-forward always has survivors; the mc annotator bootstraps the pooled
/// per-window OOS trade-R series (200 resamples, block 5). Roller: 14d IS /
/// 7d OOS / 7d step in epoch-ms, tiling the ~30-day GER40 Sept-2024 campaign
/// window. NO generalize stage: this campaign has ONE instrument (a second
/// local archive is not guaranteed), and the single-instrument refusal is
/// pinned data-free in `campaign_run_refuses_single_instrument_generalize`.
const WF_PROCESS_DOC: &str = r#"{
"format_version": 1,
"kind": "process",
@@ -722,7 +760,8 @@ const WF_PROCESS_DOC: &str = r#"{
{ "block": "std::sweep", "metric": "sqn_normalized", "select": "argmax" },
{ "block": "std::gate", "all": [ { "metric": "n_trades", "cmp": "ge", "value": 0.0 } ] },
{ "block": "std::walk_forward", "in_sample_ms": 1209600000, "out_of_sample_ms": 604800000,
"step_ms": 604800000, "mode": "rolling", "metric": "net_expectancy_r", "select": "argmax" }
"step_ms": 604800000, "mode": "rolling", "metric": "net_expectancy_r", "select": "argmax" },
{ "block": "std::monte_carlo", "resamples": 200, "block_len": 5 }
]
}"#;
@@ -769,29 +808,130 @@ fn campaign_run_unknown_id_refuses() {
);
}
/// The v1 boundary: `process register` ACCEPTS an mc-bearing document (it is
/// intrinsically valid — asserted inside `register_process_doc`); only
/// `campaign run` refuses it, at preflight, before any member runs (so no
/// data is needed), with path-addressed Debug-free prose.
/// The v2 boundary, order side: `[sweep, monte_carlo, walk_forward]` is
/// intrinsically valid — `process register` ACCEPTS it (asserted inside
/// `register_process_doc`; the intrinsic-tier ground is the shipped
/// `validate_process_permits_walk_forward_after_an_annotator` pin) — but the
/// executable pipeline shape fixes the suffix order (`std::sweep [std::gate]*
/// [std::walk_forward]? [std::monte_carlo]? [std::generalize]?`), so
/// `campaign run` refuses it at the data-free preflight, before any member
/// runs, with Debug-free PipelineShape prose.
#[test]
fn campaign_run_v1_boundary_refuses_mc_process() {
fn campaign_run_refuses_mc_before_walk_forward() {
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("mc.process.json")),
ScratchPath::File(dir.join("mc.campaign.json")),
ScratchPath::File(dir.join("mcwf.process.json")),
ScratchPath::File(dir.join("mcwf.campaign.json")),
]);
let bp_id = seed_blueprint(dir, "campaign-run-mc-seed");
let proc_id = register_process_doc(dir, "mc.process.json", MC_PROCESS_DOC);
write_doc(dir, "mc.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", ""));
let (out, code) = run_code_in(dir, &["campaign", "run", "mc.campaign.json"]);
let bp_id = seed_blueprint(dir, "campaign-run-mcwf-seed");
let proc_id = register_process_doc(dir, "mcwf.process.json", MC_BEFORE_WF_PROCESS_DOC);
write_doc(dir, "mcwf.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", ""));
let (out, code) = run_code_in(dir, &["campaign", "run", "mcwf.campaign.json"]);
assert_eq!(code, Some(1), "stdout/stderr: {out}");
assert!(out.contains("not executable in v1"), "stdout/stderr: {out}");
assert!(out.contains("std::monte_carlo"), "the prose names the block: {out}");
assert!(!out.contains("UnsupportedStage"), "Debug leak: {out}");
assert!(out.contains("process pipeline is not executable:"), "stdout/stderr: {out}");
assert!(
out.contains("std::walk_forward cannot follow std::monte_carlo"),
"the detail names the ordering violation: {out}"
);
assert!(!out.contains("not executable in v1"), "retired v1 prose resurfaced: {out}");
assert!(!out.contains("PipelineShape"), "Debug leak: {out}");
}
/// The v2 boundary, campaign side: a generalize-bearing process is an
/// executable shape, but `std::generalize` needs >= 2 instruments in the
/// campaign — a STATIC preflight fact (the referential gate has run, no data
/// is read), so the single-GER40 fixture campaign refuses with exit 1 before
/// any member runs. This is the ONE home for the single-instrument refusal;
/// the gated real-data e2e does not duplicate it (its campaign is also
/// single-instrument, and this refusal is data-free by construction).
#[test]
fn campaign_run_refuses_single_instrument_generalize() {
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("gen1.process.json")),
ScratchPath::File(dir.join("gen1.campaign.json")),
]);
let bp_id = seed_blueprint(dir, "campaign-run-gen1-seed");
let proc_id = register_process_doc(dir, "gen1.process.json", GENERALIZE_PROCESS_DOC);
write_doc(dir, "gen1.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", ""));
let (out, code) = run_code_in(dir, &["campaign", "run", "gen1.campaign.json"]);
assert_eq!(code, Some(1), "stdout/stderr: {out}");
assert!(
out.contains("std::generalize needs at least 2"),
"the refusal names the block and the minimum: {out}"
);
assert!(out.contains("1 instrument"), "the refusal names the available count: {out}");
assert!(!out.contains("GeneralizeNeedsInstruments"), "Debug leak: {out}");
}
/// An executable generalize-bearing shape whose metric is a pip metric, not
/// an R metric (`total_pips` is a known, registered scalar — `process
/// register` accepts it — but not R-denominated).
const GENERALIZE_NON_R_PROCESS_DOC: &str = r#"{
"format_version": 1,
"kind": "process",
"name": "gen-pip-screen",
"pipeline": [
{ "block": "std::sweep", "metric": "sqn_normalized", "select": "argmax" },
{ "block": "std::generalize", "metric": "total_pips" }
]
}"#;
/// The v2 boundary, metric side: `std::generalize`'s metric guard
/// (`check_r_metric`) is a STATIC preflight fact independent of the
/// instrument-arity guard above it in the same match arm — with >= 2
/// campaign instruments (so arity passes) and a pip metric, `campaign run`
/// must still refuse data-free, naming the metric, and never fall through to
/// the instrument-count prose or a Debug-formatted fault. A regression that
/// let the arity check short-circuit past the metric check (or vice versa)
/// would silently accept a pip-denominated `std::generalize`.
#[test]
fn campaign_run_refuses_generalize_non_r_metric() {
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("genpip.process.json")),
ScratchPath::File(dir.join("genpip.campaign.json")),
]);
let bp_id = seed_blueprint(dir, "campaign-run-genpip-seed");
let proc_id = register_process_doc(dir, "genpip.process.json", GENERALIZE_NON_R_PROCESS_DOC);
// Two instruments (arity passes), so the metric guard is the one that
// actually fires. The [1, 2] 1970 window never matters: this refusal is
// data-free, before any member runs.
let campaign_doc = format!(
r#"{{
"format_version": 1,
"kind": "campaign",
"name": "run-seam-genpip",
"data": {{ "instruments": ["GER40", "EURUSD"], "windows": [ {{ "from_ms": 1, "to_ms": 2 }} ] }},
"strategies": [ {{ "ref": {{ "content_id": "{bp_id}" }},
"axes": {{ "fast.length": {{ "kind": "I64", "values": [2, 4] }},
"slow.length": {{ "kind": "I64", "values": [8, 16] }} }} }} ],
"process": {{ "ref": {{ "content_id": "{proc_id}" }} }},
"seed": 7,
"presentation": {{ "persist_taps": [], "emit": [] }}
}}"#
);
write_doc(dir, "genpip.campaign.json", &campaign_doc);
let (out, code) = run_code_in(dir, &["campaign", "run", "genpip.campaign.json"]);
assert_eq!(code, Some(1), "stdout/stderr: {out}");
assert!(
out.contains("std::generalize metric \"total_pips\" is not an R metric"),
"the refusal names the offending metric: {out}"
);
assert!(!out.contains("2 instrument"), "the arity guard must not fire when arity is fine: {out}");
assert!(!out.contains("GeneralizeNonRMetric"), "Debug leak: {out}");
}
/// A v2 process doc with `std::walk_forward` following the annotator
@@ -896,51 +1036,6 @@ fn campaign_run_refuses_zero_resamples_monte_carlo_before_any_member_runs() {
assert!(!out.contains("ZeroBootstrapParam"), "Debug leak: {out}");
}
/// A v2 process doc that ends in `std::generalize`.
const GENERALIZE_PROCESS_DOC: &str = r#"{
"format_version": 1,
"kind": "process",
"name": "generalize-screen",
"pipeline": [
{ "block": "std::sweep", "metric": "sqn_normalized", "select": "argmax" },
{ "block": "std::generalize", "metric": "net_expectancy_r" }
]
}"#;
/// Property (0108 task 2): `std::generalize`'s campaign-level instrument-
/// arity guard (>= 2 instruments) is a STATIC executor-preflight refusal,
/// wired end to end from the campaign document's `data.instruments` through
/// to CLI stdout, firing before any member runs (data-free) and naming the
/// actual instrument count. `campaign_doc_json`'s fixture campaign carries
/// exactly one instrument (`GER40`), so this exercises the campaign
/// parameter `preflight` newly reads (previously unused in v1) rather than
/// anything reachable from the process document alone. A regression that
/// left the campaign parameter unread would silently accept a single-
/// instrument `std::generalize` and only fail later, opaquely, inside the
/// generalization math.
#[test]
fn campaign_run_refuses_single_instrument_generalize() {
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("gen.process.json")),
ScratchPath::File(dir.join("gen.campaign.json")),
]);
let bp_id = seed_blueprint(dir, "campaign-run-gen-seed");
let proc_id = register_process_doc(dir, "gen.process.json", GENERALIZE_PROCESS_DOC);
write_doc(dir, "gen.campaign.json", &campaign_doc_json(&bp_id, &proc_id, (1, 2), "", ""));
let (out, code) = run_code_in(dir, &["campaign", "run", "gen.campaign.json"]);
assert_eq!(code, Some(1), "stdout/stderr: {out}");
assert!(
out.contains("campaign has 1 instrument(s); std::generalize needs at least 2"),
"stdout/stderr: {out}"
);
assert!(!out.contains("GeneralizeNeedsInstruments"), "Debug leak: {out}");
}
/// Non-empty persist_taps defers LOUDLY, and the note's ORDER is pinned: it
/// prints before member execution, so it is asserted here on a run
/// that refuses at the member-data seam. The [1, 2] epoch-ms window (1970)
@@ -978,12 +1073,13 @@ fn campaign_run_persist_taps_deferred_loudly() {
}
/// Gated real-data e2e (the `cli_run.rs` skip idiom): a full
/// sweep -> gate -> walk_forward campaign over GER40 Sept-2024 where the
/// local archive is present — exit 0, emit-gated family/selection lines, a
/// final line parseable as JSON with top-level `campaign_run` linking a sweep
/// family id and a walk-forward family id, and the `campaign_runs.jsonl`
/// sibling store written. Skips with a note elsewhere so
/// `cargo test --workspace` stays green on a data-less machine.
/// sweep -> gate -> walk_forward -> monte_carlo campaign over GER40
/// Sept-2024 where the local archive is present — exit 0, emit-gated
/// family/selection lines, a final line parseable as JSON with top-level
/// `campaign_run` linking a sweep family id, a walk-forward family id, and a
/// pooled-OOS bootstrap on the terminal mc stage, and the
/// `campaign_runs.jsonl` sibling store written. Skips with a note elsewhere
/// so `cargo test --workspace` stays green on a data-less machine.
#[test]
fn campaign_run_real_e2e_sweep_gate_walkforward() {
let _fixture = project_lock();
@@ -1030,7 +1126,11 @@ fn campaign_run_real_e2e_sweep_gate_walkforward() {
let cells = v["campaign_run"]["cells"].as_array().expect("cells array");
assert_eq!(cells.len(), 1, "one (strategy, instrument, window) cell: {record_line}");
let stages = cells[0]["stages"].as_array().expect("stages array");
assert_eq!(stages.len(), 3, "sweep + gate + walk_forward realized: {record_line}");
assert_eq!(
stages.len(),
4,
"sweep + gate + walk_forward + monte_carlo realized: {record_line}"
);
assert!(
stages[0]["family_id"].as_str().is_some(),
"sweep stage links a family: {record_line}"
@@ -1044,6 +1144,21 @@ fn campaign_run_real_e2e_sweep_gate_walkforward() {
stages[2]["family_id"].as_str().is_some(),
"walk-forward stage links a family: {record_line}"
);
assert_eq!(
stages[3]["block"].as_str(),
Some("std::monte_carlo"),
"the annotator stage is realized last: {record_line}"
);
let pooled = &stages[3]["bootstrap"]["pooled_oos"];
assert!(
pooled.is_object(),
"an mc stage after walk_forward records ONE pooled-OOS bootstrap: {record_line}"
);
assert_eq!(
pooled["n_resamples"].as_u64(),
Some(200),
"the bootstrap carries the doc's resamples verbatim: {record_line}"
);
// Emit-gated lines: per-member family_table lines (4 sweep members plus
// the walk-forward OOS members) and at least one selection_report line.
assert!(
@@ -1061,6 +1176,76 @@ fn campaign_run_real_e2e_sweep_gate_walkforward() {
);
}
/// Gated real-data e2e, the OTHER mc bootstrap input shape (#200 d1): with no
/// walk_forward in the pipeline (`sweep -> monte_carlo`), the terminal
/// annotator's dual-input dispatch takes the `PerSurvivor` arm, not
/// `PooledOos` — one bootstrap per surviving sweep member, keyed by the
/// member's ordinal into the sweep family, rather than one pooled bootstrap
/// over walk-forward OOS windows. `campaign_run_real_e2e_sweep_gate_walkforward`
/// above only exercises `PooledOos`; this is the only e2e coverage of
/// `PerSurvivor` with real data (the addressing-mode tests use it only as a
/// data-free refusal fixture).
#[test]
fn campaign_run_real_e2e_sweep_monte_carlo_per_survivor() {
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("mc.process.json")),
ScratchPath::File(dir.join("mc.campaign.json")),
]);
let bp_id = seed_blueprint(dir, "campaign-run-mc-persurvivor-seed");
let proc_id = register_process_doc(dir, "mc.process.json", MC_PROCESS_DOC);
// Same GER40 Sept-2024 window as the gated wf e2e above.
write_doc(
dir,
"mc.campaign.json",
&campaign_doc_json(&bp_id, &proc_id, (1725148800000, 1727740799999), "", ""),
);
let (out, code) = run_code_in(dir, &["campaign", "run", "mc.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 e2e");
return;
}
assert_eq!(code, Some(0), "stdout/stderr: {out}");
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 cells = v["campaign_run"]["cells"].as_array().expect("cells array");
let stages = cells[0]["stages"].as_array().expect("stages array");
assert_eq!(stages.len(), 2, "sweep + monte_carlo realized: {record_line}");
assert_eq!(
stages[1]["block"].as_str(),
Some("std::monte_carlo"),
"the annotator stage is realized last: {record_line}"
);
let per_survivor = stages[1]["bootstrap"]["per_survivor"]
.as_array()
.expect("no walk_forward precedes: the PerSurvivor arm, not pooled_oos");
assert_eq!(
per_survivor.len(),
4,
"one bootstrap per surviving sweep member (4 axis combinations, no gate): {record_line}"
);
let (ordinal, bootstrap) = (per_survivor[0][0].as_u64(), &per_survivor[0][1]);
assert_eq!(ordinal, Some(0), "each entry is keyed by ordinal into the sweep family: {record_line}");
assert_eq!(
bootstrap["n_resamples"].as_u64(),
Some(100),
"the bootstrap carries the doc's resamples verbatim: {record_line}"
);
}
/// `run_campaign` resolves a target two ways — a readable file (register-then-
/// run sugar) or a bare content id (direct store address) — then funnels BOTH
/// through one shared post-resolution path: "fetch the stored canonical bytes
@@ -1069,8 +1254,11 @@ fn campaign_run_real_e2e_sweep_gate_walkforward() {
/// that property observably: registering a document and then running it once
/// by FILE and once by its own resulting content ID must refuse with the
/// byte-identical prose — proof the two addressing modes are not two
/// independently drifting code paths. No real data needed: both runs refuse
/// at the (data-free) v1-pipeline-shape preflight.
/// independently drifting code paths. No real data needed: with the [1, 2]
/// 1970 window, both runs pass the (now mc-permitting) preflight and refuse
/// at the member-data seam — no recorded geometry on a data-less host, no
/// overlapping archive window on a data-ful one — exit 1 and byte-identical
/// prose either way (the message depends only on env/instrument/window).
#[test]
fn campaign_run_by_content_id_matches_file_sugar_refusal() {
let _fixture = project_lock();
@@ -1106,6 +1294,10 @@ fn campaign_run_by_content_id_matches_file_sugar_refusal() {
let file_line = file_out.lines().find(|l| l.starts_with("aura: ")).expect("file refusal line");
let id_line = id_out.lines().find(|l| l.starts_with("aura: ")).expect("id refusal line");
assert!(
file_line.contains("no recorded geometry") || file_line.contains("no data for instrument"),
"the refusal is the member-data seam (mc processes execute now): {file_line}"
);
assert_eq!(file_line, id_line, "file- and id-addressed runs must refuse identically");
}
@@ -1115,7 +1307,9 @@ fn campaign_run_by_content_id_matches_file_sugar_refusal() {
/// observably: running the SAME registered campaign by bare id and by its
/// `content:`-prefixed form must refuse identically (proof the prefix is
/// stripped, not read as a second, unresolvable address). Both refuse at the
/// data-free v1 preflight (MC process), so no real data is needed.
/// member-data seam (the mc process is executable in v2; the [1, 2] 1970
/// window guarantees a deterministic data refusal), so no real data is
/// needed.
#[test]
fn campaign_run_tolerates_content_prefix_on_target() {
let _fixture = project_lock();
@@ -1153,6 +1347,10 @@ fn campaign_run_tolerates_content_prefix_on_target() {
"a content:-prefixed target must resolve as the bare id, not refuse as an unknown target: {pfx_line}"
);
let bare_line = bare_out.lines().find(|l| l.starts_with("aura: ")).expect("bare refusal line");
assert!(
bare_line.contains("no recorded geometry") || bare_line.contains("no data for instrument"),
"the refusal is the member-data seam (mc processes execute now): {bare_line}"
);
assert_eq!(bare_line, pfx_line, "bare-id and content:-prefixed runs must resolve identically");
}
+2 -2
View File
@@ -188,7 +188,7 @@ pub const PROCESS_BLOCKS: &[BlockSchema] = &[
},
BlockSchema {
id: "std::monte_carlo",
doc: "R-bootstrap over realised R (terminal annotator in v1)",
doc: "R-bootstrap over realised R (terminal annotator)",
slots: &[
SlotInfo { name: "resamples", kind: SlotKind::U32, required: true },
SlotInfo { name: "block_len", kind: SlotKind::U32, required: true },
@@ -196,7 +196,7 @@ pub const PROCESS_BLOCKS: &[BlockSchema] = &[
},
BlockSchema {
id: "std::generalize",
doc: "cross-instrument worst-case floor (terminal annotator in v1)",
doc: "cross-instrument worst-case floor (terminal annotator)",
slots: &[SlotInfo { name: "metric", kind: SlotKind::MetricName, required: true }],
},
];