feat(cli): aura campaign run — the executor verb over the MemberRunner driver (0107 tasks 8-9)

campaign_run.rs: target resolution (file is register-then-run sugar;
bare 64-hex is the canonical id — #198 decision 1), project gate before
any store write, zero-fault referential gate, process fetch + v1
preflight, then aura_campaign::execute with the CLI MemberRunner over
the shipped loaded-blueprint convention: per-member blueprint reload,
reduce-mode wrap, unique suffix-join of raw campaign axis names onto
the wrapped param_space, windowed real data via the shipped ms->ns
source seam (absent archive/zero-bar windows -> NoData). Emission:
family_table / selection_report lines gated on the doc's emit list
(names debug_asserted against emit_vocabulary), the campaign_run
record line always, persist_taps deferred LOUDLY on stderr before
execution (F7 lesson), zero-survivor cells noted on stderr with exit 0.
exec_fault_prose/member_fault_prose keep the Debug-free house seam.

Seam tests: 8 campaign_run_* e2e tests incl. outside-project, bogus
target, unknown id, v1-boundary (mc process registers fine, run
refuses), persist_taps ordering, and the gated real-data
sweep->gate->walkforward e2e — which ran its full assert path on this
host (local GER40 2024-09 archive) rather than the data-less skip.
Also: exec.rs campaign-id guard tightened to lowercase hex, matching
is_content_id and the store's self-keyed form (review nit).

Gates: workspace 975/0, clippy -D warnings clean.

refs #198
This commit is contained in:
2026-07-03 21:33:29 +02:00
parent b15ad07208
commit aeb0366aed
7 changed files with 907 additions and 7 deletions
+3 -1
View File
@@ -97,7 +97,9 @@ pub fn execute(
// The store's self-keying always yields a 64-hex content id; anything else
// is a caller bug — refuse it typed instead of panicking on the prefix
// slice below.
if campaign_id.len() != 64 || !campaign_id.bytes().all(|b| b.is_ascii_hexdigit()) {
if campaign_id.len() != 64
|| !campaign_id.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))
{
return Err(ExecFault::PipelineShape {
detail: format!(
"campaign_id must be a 64-hex content id (got {} chars)",