feat(cli): dissolved --trace requests only producible taps (#240)
persist_taps_from now filters the tap vocabulary through campaign_run's own tap_channel classification (exposed pub(crate)) and drops the net channel: every generated sugar campaign carries an empty cost section by construction, so net_r_equity was unproducible there and the executor's skip notice named a remedy (add a cost block to the campaign document) unreachable from the sweep/walkforward verb. The authored-campaign path keeps the notice verbatim — there the user wrote the document and the remedy is reachable. The two full-vocabulary pins move to the producible subset (deliberate pin move, documented in their doc comments). closes #240
This commit is contained in:
@@ -640,7 +640,7 @@ fn present_campaign(
|
||||
/// (tx_net, #234) — produced only when the campaign document carries a cost
|
||||
/// block (the caller's producibility check).
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum TapChannel {
|
||||
pub(crate) enum TapChannel {
|
||||
Equity,
|
||||
Exposure,
|
||||
REquity,
|
||||
@@ -650,7 +650,7 @@ enum TapChannel {
|
||||
/// Route one requested tap of the closed vocabulary to its drained channel;
|
||||
/// `None` marks a name outside the vocabulary (unreachable — `validate_campaign`
|
||||
/// refuses it — and mapped defensively rather than guessed at).
|
||||
fn tap_channel(tap: &str) -> Option<TapChannel> {
|
||||
pub(crate) fn tap_channel(tap: &str) -> Option<TapChannel> {
|
||||
// The emit_vocabulary debug_assert's twin: every vocabulary tap must be
|
||||
// routable here — a future fifth vocabulary entry fails loudly instead of
|
||||
// silently skipping.
|
||||
|
||||
@@ -46,12 +46,22 @@ pub(crate) struct SugarInvocation<'a> {
|
||||
}
|
||||
|
||||
/// The generated campaign's `persist_taps` section for one invocation:
|
||||
/// the full closed tap vocabulary when `--trace` was requested, else empty
|
||||
/// (the #168 no-op — this is the one seam `translate_sweep`/
|
||||
/// `translate_walkforward` share for the decision).
|
||||
/// when `--trace` was requested, the tap vocabulary MINUS the net channel
|
||||
/// (`net_r_equity`), else empty (the #168 no-op — this is the one seam
|
||||
/// `translate_sweep`/`translate_walkforward` share for the decision). Every
|
||||
/// generated sugar campaign carries an empty cost section (`cost: vec![]`)
|
||||
/// by construction, so the net channel is never producible there; requesting
|
||||
/// it anyway would make `campaign_run`'s executor print an unreachable "add a
|
||||
/// cost block to the campaign document" remedy (#240). The exclusion reuses
|
||||
/// `campaign_run`'s own tap/channel classification (`tap_channel`/
|
||||
/// `TapChannel::Net`) rather than duplicating the net-tap name here.
|
||||
fn persist_taps_from(trace: bool) -> Vec<String> {
|
||||
if trace {
|
||||
tap_vocabulary().iter().map(|t| t.to_string()).collect()
|
||||
tap_vocabulary()
|
||||
.iter()
|
||||
.filter(|t| crate::campaign_run::tap_channel(t) != Some(crate::campaign_run::TapChannel::Net))
|
||||
.map(|t| t.to_string())
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
@@ -742,19 +752,28 @@ mod tests {
|
||||
assert!(err.contains("mixed value kinds"), "{err}");
|
||||
}
|
||||
|
||||
/// Property (#224): `SugarInvocation.trace = true` requests the FULL closed
|
||||
/// tap vocabulary on the generated campaign's `presentation.persist_taps` —
|
||||
/// the honest inverse of the #168 no-op, where `persist_taps` stayed `vec![]`
|
||||
/// regardless of `--trace`. `trace = false` (the default, pinned by the
|
||||
/// sibling `translate_sweep_is_deterministic_and_names_flow`) keeps the
|
||||
/// vocabulary empty.
|
||||
/// Property (#224, pin moved to #240): `SugarInvocation.trace = true`
|
||||
/// requests the tap vocabulary MINUS the net channel on the generated
|
||||
/// campaign's `presentation.persist_taps` — the honest inverse of the
|
||||
/// #168 no-op (where `persist_taps` stayed `vec![]` regardless of
|
||||
/// `--trace`), narrowed to what the generated campaign's own empty cost
|
||||
/// section can actually produce (#240: `net_r_equity` is unproducible
|
||||
/// there and must be excluded). `trace = false` (the default, pinned by
|
||||
/// the sibling `translate_sweep_is_deterministic_and_names_flow`) keeps
|
||||
/// the vocabulary empty. Kept as its own translator-level pin alongside
|
||||
/// the cross-translator `dissolved_trace_requests_only_producible_taps_
|
||||
/// excluding_the_net_channel` property test (#240): that test pins the
|
||||
/// shared `persist_taps_from` seam once for both translators together,
|
||||
/// this one keeps `translate_sweep`'s own regression independent of the
|
||||
/// walkforward sibling so a translator-specific wiring break is not
|
||||
/// masked by the other translator still passing.
|
||||
#[test]
|
||||
fn translate_sweep_trace_requests_the_full_tap_vocabulary() {
|
||||
fn translate_sweep_trace_requests_the_producible_tap_subset() {
|
||||
let ax = axes();
|
||||
let mut i = inv(&ax, "probe", &["GER40"], None, "{\"bp\":1}");
|
||||
i.trace = true;
|
||||
let g = translate_sweep(&i).unwrap();
|
||||
let want: Vec<String> = aura_research::tap_vocabulary().iter().map(|t| t.to_string()).collect();
|
||||
let want: Vec<String> = vec!["equity".to_string(), "exposure".to_string(), "r_equity".to_string()];
|
||||
assert_eq!(g.campaign.presentation.persist_taps, want);
|
||||
}
|
||||
|
||||
@@ -940,16 +959,24 @@ mod tests {
|
||||
assert!(aura_campaign::preflight(&a.process, &a.campaign).is_ok());
|
||||
}
|
||||
|
||||
/// Property (#224): mirrors `translate_sweep_trace_requests_the_full_tap_vocabulary`
|
||||
/// for walkforward's translator — the walkforward sibling refusal lifts
|
||||
/// identically, riding the same `persist_taps_from` seam.
|
||||
/// Property (#224, pin moved to #240): mirrors
|
||||
/// `translate_sweep_trace_requests_the_producible_tap_subset` for
|
||||
/// walkforward's translator — the walkforward sibling refusal lifts
|
||||
/// identically, riding the same `persist_taps_from` seam, and the same
|
||||
/// net-channel exclusion applies (#240: the generated campaign's empty
|
||||
/// cost section cannot produce `net_r_equity`). Kept as its own
|
||||
/// translator-level pin alongside the cross-translator
|
||||
/// `dissolved_trace_requests_only_producible_taps_excluding_the_net_
|
||||
/// channel` property test for the same reason as the sweep sibling: a
|
||||
/// walkforward-specific wiring break must not be masked by sweep alone
|
||||
/// still passing.
|
||||
#[test]
|
||||
fn translate_walkforward_trace_requests_the_full_tap_vocabulary() {
|
||||
fn translate_walkforward_trace_requests_the_producible_tap_subset() {
|
||||
let ax = wf_axes();
|
||||
let mut i = inv(&ax, "walkforward", &["GER40"], Some(VolStop { length: 14, k: 2.0 }), "{\"bp\":1}");
|
||||
i.trace = true;
|
||||
let g = translate_walkforward(&i, "sqn_normalized", WF_W, SelectRule::Argmax).unwrap();
|
||||
let want: Vec<String> = aura_research::tap_vocabulary().iter().map(|t| t.to_string()).collect();
|
||||
let want: Vec<String> = vec!["equity".to_string(), "exposure".to_string(), "r_equity".to_string()];
|
||||
assert_eq!(g.campaign.presentation.persist_taps, want);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user