feat(aura-engine, aura-vocabulary, aura-cli): the args channel end to end
The data plane reaches non-scalar structural config (closes #271, the final Data-authorability-boundary item). Op::Add gains args (string pairs, applied via try_args after resolve and before the bind loop — no pending builder ever enters a graph; an arg-bearing type without args refuses as MissingArg). OpError::BadArg carries the precise ArgOpError; the CLI renders it as prose naming the arg, its kind, and the kind's closed hint (exit 1, #175 content-fault class). Serde: PrimitiveData.args (skip-if-empty — args-free documents stay byte-identical, golden-pinned, so every existing content id is stable per C18) with a data-driven format version: the writer emits 1 for args-free documents and 2 when any primitive recursively carries args; the loader accepts 1..=2. The old v2-refusal pin flips deliberately to v3 (named contract change). Identity JSON keeps args — they are structural, and the Rust configured() path and the args op-script bridge to the same identity id (pinned). Roster: Session / LinComb / CostSum enter (33 -> 36, both count pins; the LinComb-rejection assertion flips to the resolved side). introspect --node shows arg rows (name, kind, hint) plus the pending note; OP_REFERENCE's add row teaches the args form. Registry comment de-staled (LinComb is now roster-reachable). Sequencing note: this commit lands op seam and roster together so no intermediate tree state exposes a pending builder.
This commit is contained in:
@@ -170,8 +170,9 @@ fn graph_introspect_vocabulary_lists_exactly_the_closed_roster_count() {
|
||||
let lines: Vec<&str> = stdout.lines().filter(|l| !l.is_empty()).collect();
|
||||
assert_eq!(
|
||||
lines.len(),
|
||||
33,
|
||||
"the std-only (no project) vocabulary has exactly the roster's 33 entries: {stdout}"
|
||||
36,
|
||||
"the std-only (no project) vocabulary has exactly the roster's 36 entries (#271: \
|
||||
Session/LinComb/CostSum moved in): {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -183,6 +184,78 @@ fn graph_introspect_node_answers_ports_without_a_build() {
|
||||
assert!(stdout.contains("value"), "lists the output field: {stdout}");
|
||||
}
|
||||
|
||||
/// #271 acceptance criterion 1: an op-script reaching BOTH new arg-bearing
|
||||
/// roster entries (a `Session` for any IANA timezone/open, a `LinComb` of any
|
||||
/// arity — no Rust authored) builds via `aura graph build`; the emitted
|
||||
/// blueprint is `"version": 2` (the args-bearing tier) and carries the
|
||||
/// accepted timezone verbatim.
|
||||
///
|
||||
/// Deviation from the spec's literal worked example
|
||||
/// (`docs/specs/construction-args.md` §"The user-facing program"): that JSON
|
||||
/// wires `ny.bars_since_open` (`Session`'s I64 output) directly into
|
||||
/// `mix.term[0]` (`LinComb`'s F64 input) — a genuine kind mismatch the
|
||||
/// engine's eager `connect` gate refuses (I64 != F64), not a property of
|
||||
/// this feature. This fixture keeps every construction-args property the
|
||||
/// criterion asks for (Session + args, LinComb + args, version 2, tz
|
||||
/// carried) but feeds `LinComb` from two same-kind SMAs and exposes
|
||||
/// `ny.bars_since_open` at the boundary directly (`expose` has no kind
|
||||
/// constraint) instead of wiring it into the mismatched slot.
|
||||
#[test]
|
||||
fn graph_build_accepts_the_session_args_example() {
|
||||
let doc = r#"[
|
||||
{"op":"source","role":"price","kind":"F64"},
|
||||
{"op":"add","type":"Session","name":"ny",
|
||||
"args":{"tz":"America/New_York","open":"09:30"},
|
||||
"bind":{"period_minutes":{"I64":15}}},
|
||||
{"op":"add","type":"SMA","name":"fast","bind":{"length":{"I64":2}}},
|
||||
{"op":"add","type":"SMA","name":"slow","bind":{"length":{"I64":4}}},
|
||||
{"op":"add","type":"LinComb","name":"mix","args":{"arity":"2"},
|
||||
"bind":{"weights[0]":{"F64":1.0},"weights[1]":{"F64":0.25}}},
|
||||
{"op":"add","type":"Bias"},
|
||||
{"op":"feed","role":"price","into":["ny.trigger","fast.series","slow.series"]},
|
||||
{"op":"connect","from":"fast.value","to":"mix.term[0]"},
|
||||
{"op":"connect","from":"slow.value","to":"mix.term[1]"},
|
||||
{"op":"connect","from":"mix.value","to":"bias.signal"},
|
||||
{"op":"expose","from":"bias.bias","as":"bias"},
|
||||
{"op":"expose","from":"ny.bars_since_open","as":"session_bar"},
|
||||
{"op":"doc","text":"NY-session-gated SMA blend"}
|
||||
]"#;
|
||||
let (stdout, stderr, ok) = run(&["graph", "build"], doc);
|
||||
assert!(ok, "the worked example builds: {stderr}");
|
||||
assert!(stdout.contains(r#""format_version":2"#), "an args-bearing document is version 2: {stdout}");
|
||||
assert!(stdout.contains("America/New_York"), "carries the accepted tz verbatim: {stdout}");
|
||||
}
|
||||
|
||||
/// #271: a malformed construction-arg value (a bad IANA tz string) refuses at
|
||||
/// the `add` op with exit 1, naming the offending arg (not a Debug leak).
|
||||
#[test]
|
||||
fn graph_build_bad_tz_is_exit_1_naming_the_arg() {
|
||||
let doc = r#"[
|
||||
{"op":"add","type":"Session","name":"ny",
|
||||
"args":{"tz":"not_a_zone","open":"09:30"}}
|
||||
]"#;
|
||||
let (stderr, code) = run_code(&["graph", "build"], doc);
|
||||
assert_eq!(code, Some(1), "a construction-arg fault is exit 1: {stderr}");
|
||||
assert!(stderr.contains("tz"), "names the offending arg: {stderr}");
|
||||
assert!(!stderr.contains("{"), "no Debug-struct leak: {stderr}");
|
||||
}
|
||||
|
||||
/// #271: `graph introspect --node Session` self-describes its declared
|
||||
/// construction args (name, kind, hint) plus the pending note — the
|
||||
/// discovery surface an author reads before writing the `args` object.
|
||||
#[test]
|
||||
fn graph_introspect_node_session_lists_arg_rows() {
|
||||
let (stdout, _stderr, ok) = run(&["graph", "introspect", "--node", "Session"], "");
|
||||
assert!(ok, "exit success");
|
||||
assert!(stdout.contains("tz"), "lists the tz arg: {stdout}");
|
||||
assert!(stdout.contains("open"), "lists the open arg: {stdout}");
|
||||
assert!(stdout.contains("IANA timezone"), "shows the tz hint: {stdout}");
|
||||
assert!(
|
||||
stdout.contains("ports and params form at construction"),
|
||||
"shows the pending note: {stdout}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graph_introspect_unwired_reports_open_slots() {
|
||||
let doc = r#"[
|
||||
|
||||
Reference in New Issue
Block a user