test(cli): RED — run manifest stamps untouched bound defaults (refs #249)

Executable spec for the defaults field: aura run on the fully bound
examples/r_sma.json must report a manifest whose params stays [] (the
"what varied" record) and whose new defaults field carries the
untouched bound params as wrap-prefixed (name, Scalar) pairs in
bound_param_space() order — fast.length I64 2, slow.length I64 4,
bias.scale F64 0.5. Asserts on the stdout JSON (C14: stdout shape ==
on-disk shape), so it compiles against the current RunManifest and
fails on the absent key.
This commit is contained in:
2026-07-13 13:41:21 +02:00
parent 487431d97d
commit 4e6d5d4688
+34
View File
@@ -141,6 +141,40 @@ fn run_prints_json_and_exits_zero() {
assert!(line.contains("\"r\":{\"expectancy_r\":"), "got: {line}");
}
/// Property (#249): a plain `aura run` on a fully bound blueprint records the
/// untouched bound param values *directly in the outcome artifact*, so a raw
/// reader of the manifest recovers the effective params without dereferencing
/// the topology hash into the content-addressed store and re-implementing
/// aura's merge semantics. The manifest carries a `defaults` field — the bound
/// params merged inside `PrimitiveBuilder::build` below every record-writing
/// layer — as wrap-prefixed `(name, Scalar)` pairs in `bound_param_space()`
/// order (the same coordinate `--axis` uses). `params` keeps its "what varied"
/// meaning: on a plain run nothing varied, so it stays `[]`, and every bound
/// value lands in `defaults` instead of vanishing from the record.
#[test]
fn run_manifest_stamps_untouched_bound_defaults() {
// The shipped r_sma.json is fully bound (fast.length=2, slow.length=4,
// bias.scale=0.5); no axis re-opens any of them, so none flow through the
// param space — today they appear in no outcome artifact ("params":[]).
let out = Command::new(BIN).args(["run", "examples/r_sma.json"]).output().expect("spawn aura run");
assert!(out.status.success(), "exit status: {:?}", out.status);
let stdout = String::from_utf8(out.stdout).expect("utf-8 stdout");
let line = stdout.trim_end();
// "what varied" is empty on a plain run — the bound defaults are NOT params.
assert!(line.contains("\"params\":[]"), "params stays empty on a plain run: {line}");
// The untouched bound values are stamped in `defaults`, wrap-prefixed with
// the blueprint name and ordered by `bound_param_space()` (node-declaration
// order: fast, slow, bias), each a self-describing tagged Scalar (C14).
assert!(
line.contains(
r#""defaults":[["sma_signal.fast.length",{"I64":2}],["sma_signal.slow.length",{"I64":4}],["sma_signal.bias.scale",{"F64":0.5}]]"#
),
"manifest must carry the untouched bound defaults: {line}",
);
}
/// Property: the RunManifest is self-identifying — its `commit` carries the
/// real code identity that produced the run, not a placeholder. C8/C18 audit
/// trail (this run = this commit): once runs are archived, `manifest.commit`