diff --git a/crates/aura-cli/src/campaign_run.rs b/crates/aura-cli/src/campaign_run.rs index 9cd99f6..e95f8f0 100644 --- a/crates/aura-cli/src/campaign_run.rs +++ b/crates/aura-cli/src/campaign_run.rs @@ -345,6 +345,19 @@ pub(crate) fn run_campaign(target: &str, env: &Env) -> Result<(), String> { run_campaign_by_id(&campaign_id, env, RunPresentation::Full) } +/// An executed campaign plus the context its presenter and the dissolved-verb +/// sugar consume: `outcome` (records + per-cell realizations) and the +/// `campaign` / `strategies` / `server` the emit + trace-persistence tail need. +/// Returned by [`run_campaign_returning`] so a caller can read the outcome +/// without the stdout tail (a forthcoming dissolved-verb sugar path — the +/// generalize translator's own runner lands in a later task). +pub(crate) struct CampaignRun { + pub outcome: aura_campaign::CampaignOutcome, + pub campaign: CampaignDoc, + pub strategies: Vec<(String, String)>, + pub server: Arc, +} + /// The one campaign executor path from a resolved content id onward: fetch /// the stored canonical bytes by id (so file addressing and id addressing /// produce the same realization by construction) and re-run the intrinsic @@ -359,6 +372,22 @@ pub(crate) fn run_campaign_by_id( env: &Env, presentation: RunPresentation, ) -> Result<(), String> { + let run = run_campaign_returning(campaign_id, env)?; + present_campaign(run, presentation, env) +} + +/// The one campaign executor path from a resolved content id up to (not +/// including) presentation: fetch the stored canonical bytes by id (so file +/// addressing and id addressing produce the same realization by construction), +/// re-run the intrinsic tier, resolve strategies, execute — returning the +/// outcome bundled with the context the presenter needs. Shared by +/// `run_campaign_by_id` (which then presents) and a forthcoming dissolved-verb +/// sugar path that reads the outcome and self-prints (lands in a later task, +/// alongside the generalize translator's own runner). +pub(crate) fn run_campaign_returning( + campaign_id: &str, + env: &Env, +) -> Result { let registry = env.registry(); let campaign_text = registry .get_campaign(campaign_id) @@ -438,6 +467,20 @@ pub(crate) fn run_campaign_by_id( ) .map_err(|f| exec_fault_prose(&f))?; + Ok(CampaignRun { outcome, campaign, strategies, server: runner.server }) +} + +/// Emit an executed campaign's results per `presentation`: the zero-survivor +/// stderr notes, the emit-gated family/selection lines, the always-on record +/// line (Full only), then trace persistence. Split out of `run_campaign_by_id` +/// so the dissolved-verb sugar can consume the outcome without this stdout tail. +fn present_campaign( + run: CampaignRun, + presentation: RunPresentation, + env: &Env, +) -> Result<(), String> { + let CampaignRun { outcome, campaign, strategies, server } = run; + // Zero-survivor stderr notes (exit stays 0 — a null result is a valid // research result, #198 decision 8). Addressed by the fields the record // already carries (strategy/instrument/window_ms), not by re-deriving a @@ -516,7 +559,7 @@ pub(crate) fn run_campaign_by_id( &outcome, &campaign, &strategies, - &runner.server, + &server, env, )?; }