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:
@@ -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}")
|
||||
|
||||
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user