feat(cli): campaign validate runs the executor preflight as a third tier (fieldtest 0108 F6)

valid == runnable for every pure document/campaign property: inside a
project, once intrinsic + referential pass, validate fetches the
referenced process from the store and runs the data-free
aura_campaign::preflight (promoted to pub) — an executable shape gains
'campaign document valid (executable): pipeline shape and static
guards pass'; a shape/param/metric/instrument fault refuses under
'campaign is not executable:' with the exec_fault_prose detail, exit 1,
before any member runs. Outside a project the skip line is unchanged
(the third tier needs the store like the second).

Collateral the RED author predicted: the referential e2e's OK fixture
(PROCESS_DOC: deflate+plateau sweep, overfit_probability gate) was
intrinsically-valid-but-never-executable — that test now registers an
executable twin (argmax/no-deflate, per-member gate metric); the base
PROCESS_DOC fixture stays untouched for all other consumers.

RED-first (tdd-author handoff): the third-tier pin observed failing on
the two-line validate output.

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

closes #205
This commit is contained in:
2026-07-04 02:08:28 +02:00
parent 197936b27d
commit 38ffe50b57
4 changed files with 113 additions and 4 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ pub(crate) fn is_content_id(s: &str) -> bool {
/// Phrase an [`ExecFault`] for stderr — Debug-leak-free, path-addressed
/// (stage index + block id), the `doc_fault_prose`/`ref_fault_prose` register.
fn exec_fault_prose(f: &ExecFault) -> String {
pub(crate) fn exec_fault_prose(f: &ExecFault) -> String {
match f {
ExecFault::PipelineShape { detail } => {
format!("process pipeline is not executable: {detail}")
+23
View File
@@ -375,6 +375,29 @@ fn validate_campaign_file(file: &PathBuf, env: &Env) -> Result<(), String> {
));
}
println!("campaign document valid (referential): all references resolve, axes are in the param space");
// Third tier (#205): the executor's data-free static preflight, so
// "valid" means "runnable" for every pure document/campaign property.
// The referential tier just proved the process ref resolves, so the
// fetch cannot miss short of a store race.
let proc_id = match &doc.process.r#ref {
aura_research::DocRef::ContentId(id) | aura_research::DocRef::IdentityId(id) => {
id.clone()
}
};
let proc_text = env
.registry()
.get_process(&proc_id)
.map_err(|e| e.to_string())?
.ok_or_else(|| format!("no process {proc_id} in the project store"))?;
let process = parse_process(&proc_text)
.map_err(|e| doc_error_prose("stored process document", &e))?;
if let Err(f) = aura_campaign::preflight(&process, &doc) {
return Err(fault_block(
"campaign is not executable:",
vec![crate::campaign_run::exec_fault_prose(&f)],
));
}
println!("campaign document valid (executable): pipeline shape and static guards pass");
} else {
let cwd = std::env::current_dir()
.map(|d| d.display().to_string())