feat(aura-cli, aura-runner): exec routing-seam refusals + by-name unbound-role prose

Harvest sweep, batch 2 of 6.

- exec's file-target classification is now a four-way shape peek
  (campaign / blueprint / op-script / wrong-kind): a valid-JSON op-script
  array refuses at the routing seam with a build-first pointer ('aura
  graph build') instead of the blueprint leg's false 'not valid JSON'
  claim, and a kind-bearing non-campaign document names the found kind
  plus exec's two executable classes. Exit-code classes unchanged
  (op-script usage-class 2, wrong-kind runtime-class 1).
- the bare-tap measurement leg renders CompileError::UnboundRootRole by
  the signal's own root-role NAME (captured pre-consumption), retiring
  the raw Debug-form leak; every other variant keeps the total fallback.

refs #342
refs #339
This commit is contained in:
2026-07-26 11:47:21 +02:00
parent 6b6086fdef
commit 521459dd50
4 changed files with 186 additions and 39 deletions
+45
View File
@@ -258,6 +258,51 @@ fn exec_malformed_json_file_refuses_neutrally_not_misrouted() {
);
}
/// #342 item 3: a valid-JSON op-script (a top-level JSON array of
/// construction ops) handed to `exec` must not fall through to the
/// blueprint leg's "blueprint document is not valid JSON" refusal — that
/// claim is false, the document IS valid JSON, just the wrong shape. It
/// refuses right at the routing seam with prose that names the op-script
/// diagnosis and points at the build step that turns it into a runnable
/// blueprint (`aura graph build`).
#[test]
fn exec_op_script_file_refuses_with_build_hint_not_invalid_json() {
let dir = temp_cwd("exec-op-script");
let op_script = r#"[
{"op":"source","role":"price","kind":"F64"},
{"op":"add","type":"SMA","name":"fast","bind":{"length":{"I64":2}}},
{"op":"feed","role":"price","into":["fast.series"]},
{"op":"expose","from":"fast.value","as":"bias"}
]"#;
write_doc(&dir, "script.json", op_script);
let (out, code) = run_code_in(&dir, &["exec", "script.json"]);
assert_eq!(code, Some(2), "stdout/stderr: {out}");
assert!(out.contains("op-script"), "must name the op-script diagnosis: {out}");
assert!(out.contains("graph build"), "must point at the build step: {out}");
assert!(
!out.contains("not valid JSON"),
"the document IS valid JSON — must not claim otherwise: {out}"
);
}
/// #342 item 4: a kind-bearing document that is not a campaign (here, a
/// process document) refuses naming the found kind and exec's two
/// executable classes, instead of the campaign leg's generic "the \"kind\"
/// key must be \"campaign\"" — which never says WHAT was found, nor what
/// exec accepts at all.
#[test]
fn exec_process_document_names_found_kind_and_executable_classes() {
let dir = temp_cwd("exec-wrong-kind-process");
write_doc(&dir, "p.process.json", SWEEP_ONLY_PROCESS_DOC);
let (out, code) = run_code_in(&dir, &["exec", "p.process.json"]);
assert_eq!(code, Some(1), "stdout/stderr: {out}");
assert!(out.contains("\"process\""), "must name the found kind: {out}");
assert!(
out.contains("campaign document") && out.contains("fully-bound blueprint"),
"must name both of exec's executable classes: {out}"
);
}
// ---------------------------------------------------------------------------
// Task 2: the exec blueprint leg (synthetic single run).
// ---------------------------------------------------------------------------
+6 -6
View File
@@ -2312,10 +2312,9 @@ fn graph_build_accepts_an_open_input_pattern() {
/// through `wrap_r` (an existing, unrelated nesting mechanism unaffected by
/// this cycle), so a bare-tap (no-`bias`) pattern is used here: its
/// `run_measurement` path compiles the signal directly, hitting
/// `CompileError::UnboundRootRole` at bootstrap — via the EXISTING `{e:?}`
/// Debug rendering (a raw index, not a role name); a name mapping there is
/// left for a follow-up (out of this cycle's scope, per the plan's own
/// escape hatch), so this pins the existing form.
/// `CompileError::UnboundRootRole` at bootstrap — rendered by role NAME
/// (#342 item 3), not the raw flat index the earlier form left as a
/// follow-up.
#[test]
fn running_an_open_blueprint_refuses_at_bootstrap() {
let doc = r#"[
@@ -2333,7 +2332,8 @@ fn running_an_open_blueprint_refuses_at_bootstrap() {
assert_eq!(code, Some(1), "an open blueprint refuses standalone at bootstrap, not finish: {stdout} {stderr}");
assert!(stdout.is_empty(), "no report emitted on a bootstrap refusal: {stdout}");
assert!(
stderr.contains("UnboundRootRole"),
"the runnability gate (compile), unchanged, names the fault: {stderr}"
stderr.contains("root role \"price\" is unbound"),
"the runnability gate (compile), unchanged, now names the role, not a raw index: {stderr}"
);
assert!(!stderr.contains("UnboundRootRole"), "Debug leak: {stderr}");
}