feat(aura-cli): exec — the one executor verb, campaign legs + blueprint single run

The #319 retirement's constructive half, slice 1: a new top-level
`exec <target>` executes a campaign document (file, register-then-run, or
64-hex content id — byte-behaviour of the retired `campaign run`, which no
longer parses) or a fully-bound signal blueprint (synthetic single run,
record line byte-identical to `aura run`'s, root-name gate migrated).
Flag discipline is per-leg: --parallel-instruments on campaign targets,
--tap on blueprint targets (refused on campaign targets with prose).
File routing peeks the document's top-level "kind" key: is_blueprint_file
alone cannot tell two .json classes apart.

--override (the #246 residue) and the measurement leg follow in later
slices; run/sweep/walkforward/mc/generalize stay alive until the removal
slice.

refs #319
This commit is contained in:
2026-07-25 18:29:16 +02:00
parent 9cfe2965c0
commit 78b80ec0fd
5 changed files with 572 additions and 70 deletions
+1 -26
View File
@@ -65,7 +65,7 @@ impl DocIntrospectCmd {
/// Parse `--parallel-instruments` in domain terms: clap's built-in
/// `NonZeroUsize` parser would leak the Rust type name ("number would be
/// zero for non-zero type") at exactly the moment a user needs guidance.
fn parse_parallel_instruments(s: &str) -> Result<std::num::NonZeroUsize, String> {
pub(crate) fn parse_parallel_instruments(s: &str) -> Result<std::num::NonZeroUsize, String> {
s.parse::<usize>().ok().and_then(std::num::NonZeroUsize::new).ok_or_else(|| {
"must be a whole number of at least 1 — it bounds how many distinct \
instruments are resident in parallel"
@@ -411,19 +411,6 @@ pub enum CampaignSub {
Introspect(DocIntrospectCmd),
/// Register a valid campaign document into the store under the runs root.
Register { file: PathBuf },
/// Execute a stored campaign into a realized run-set (a .json file is
/// register-then-run sugar; the canonical address is the content id).
Run {
target: String,
/// Bound on distinct instruments resident in parallel (the RAM
/// lever; 1 reproduces the sequential loop's footprint).
#[arg(
long,
default_value_t = aura_campaign::DEFAULT_PARALLEL_INSTRUMENTS,
value_parser = parse_parallel_instruments,
)]
parallel_instruments: std::num::NonZeroUsize,
},
/// List stored campaign realizations, or dump one campaign's records
/// (the bare store lines, not the run-emit wrapper).
Runs { campaign: Option<String>, run: Option<usize> },
@@ -542,18 +529,7 @@ fn campaign_runs(campaign: Option<&str>, run: Option<usize>, env: &Env) -> Resul
Ok(())
}
/// #272: `Run` threads a failed-cell count (exit 3 on completed-with-failures,
/// via `exit_on_campaign_result`), so it is pulled out of the unified
/// `Result<(), String>` match the other four subcommands still share.
pub fn campaign_cmd(cmd: CampaignCmd, env: &Env) {
if let CampaignSub::Run { target, parallel_instruments } = &cmd.sub {
crate::exit_on_campaign_result(crate::campaign_run::run_campaign(
target,
env,
*parallel_instruments,
));
return;
}
let result = match &cmd.sub {
CampaignSub::Validate { file } => validate_campaign_file(file, env),
CampaignSub::Introspect(i) => {
@@ -563,7 +539,6 @@ pub fn campaign_cmd(cmd: CampaignCmd, env: &Env) {
CampaignSub::Register { file } => register_campaign(file, env),
CampaignSub::Runs { campaign, run } => campaign_runs(campaign.as_deref(), *run, env),
CampaignSub::Show { id } => show_campaign(id, env),
CampaignSub::Run { .. } => unreachable!("handled above"),
};
if let Err(m) = result {
eprintln!("aura: {m}");