From 2be6cf8860a0899fa8dab43599a83f3ff8e73d24 Mon Sep 17 00:00:00 2001 From: Brummel Date: Sat, 11 Jul 2026 16:16:04 +0200 Subject: [PATCH] feat(cli): dissolved --trace requests only producible taps (#240) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- crates/aura-cli/src/campaign_run.rs | 4 +- crates/aura-cli/src/verb_sugar.rs | 61 +++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/crates/aura-cli/src/campaign_run.rs b/crates/aura-cli/src/campaign_run.rs index 7de560c..5493005 100644 --- a/crates/aura-cli/src/campaign_run.rs +++ b/crates/aura-cli/src/campaign_run.rs @@ -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 { +pub(crate) fn tap_channel(tap: &str) -> Option { // 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. diff --git a/crates/aura-cli/src/verb_sugar.rs b/crates/aura-cli/src/verb_sugar.rs index dcad786..e88d8e5 100644 --- a/crates/aura-cli/src/verb_sugar.rs +++ b/crates/aura-cli/src/verb_sugar.rs @@ -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 { 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 = aura_research::tap_vocabulary().iter().map(|t| t.to_string()).collect(); + let want: Vec = 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 = aura_research::tap_vocabulary().iter().map(|t| t.to_string()).collect(); + let want: Vec = vec!["equity".to_string(), "exposure".to_string(), "r_equity".to_string()]; assert_eq!(g.campaign.presentation.persist_taps, want); }