1baee774bb
Decide #310's data-authorability fork as (b), split by run-mode authority: selecting a Named fold subscription is data-reachable on the one-shot run path; adding a fold stays a Rust entry (role 2, C25). `aura run` gains a repeatable --tap TAP=FOLD selector feeding the existing TapPlan seam: no flag keeps the record-all default; any flag replaces the plan entirely (unlisted taps stay unbound — C27 inert, non-error). Validation stays in bind_tap_plan (roster-enumerating label refusal, typed UndeclaredTap, both before store I/O); parse-level refusals (malformed pair, duplicate tap) exit 2 via the verb's usage convention. Rejected alternatives (minuted on #310 with the skeptic-pass rationale): an op-script/blueprint carrier (C27 forbids recording policy in the serialized fragment); ratifying Rust-only (softens a designed-in promise with effort as the only rationale). The campaign/document carrier and the persist_taps/declared-tap namespace reconciliation are deferred to the Measurement-reachable milestone (#312/#327, minuted on #312). Ledger: C27 Current state records the boundary; the superseded sentence moved verbatim to the new c27-declared-taps.history.md sidecar; the glossary tap-plan paragraph updated, fixing the pre-existing inaccuracy that named `aura measure` a record-all-passing verb (it is the post-hoc IC analysis and constructs no tap plan). Verification: 5 new unit tests (parse branches), 5 new binary-level selector tests incl. a byte-identity pin of an explicit all-record plan vs the record-all default (C1), 1 measurement-arm test; full workspace suite green; clippy -D warnings clean. Independent opus diff review: approved, no Important/Minor findings (nits: inert-arm generic refusal, a=b=c falling to the roster refusal — accepted as-is). closes #310
84 lines
4.1 KiB
Rust
84 lines
4.1 KiB
Rust
//! #286 / C28 phase 3: `aura run` on a measurement-shaped blueprint (declares a
|
|
//! tap, exposes no `bias`) runs BARE — no wrap_r scaffold — emits a
|
|
//! `MeasurementReport` and persists the tapped series. A taps-only blueprint is
|
|
//! refused before this feature; this pins the new additive behaviour.
|
|
|
|
use std::path::Path;
|
|
use std::process::Command;
|
|
|
|
const BIN: &str = env!("CARGO_BIN_EXE_aura");
|
|
|
|
fn temp_cwd(name: &str) -> std::path::PathBuf {
|
|
let dir = Path::new(env!("CARGO_TARGET_TMPDIR")).join(format!("aura-cli-meas-{name}"));
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
std::fs::create_dir_all(&dir).expect("create temp cwd");
|
|
dir
|
|
}
|
|
|
|
/// The shipped `examples/r_sma.json`, turned MEASUREMENT-shaped: one declared tap
|
|
/// on node 0 ("fast", SMA(2)) field 0, and its `bias` output removed. Same closed
|
|
/// topology (`sma_signal`), so it runs on the built-in synthetic stream.
|
|
fn measurement_blueprint_json() -> String {
|
|
let path = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
|
let doc = std::fs::read_to_string(path).expect("read examples/r_sma.json");
|
|
let mut v: serde_json::Value = serde_json::from_str(&doc).expect("parse r_sma.json");
|
|
v["blueprint"]["taps"] = serde_json::json!([{"name": "fast_tap", "from": {"node": 0, "field": 0}}]);
|
|
v["blueprint"]["output"] = serde_json::json!([]); // no `bias` — a measurement, not a strategy
|
|
serde_json::to_string(&v).expect("re-serialize measurement blueprint")
|
|
}
|
|
|
|
#[test]
|
|
fn measurement_blueprint_runs_bare_and_emits_its_tap() {
|
|
let cwd = temp_cwd("runs-bare");
|
|
let bp_path = cwd.join("measurement.json");
|
|
std::fs::write(&bp_path, measurement_blueprint_json()).expect("write measurement blueprint");
|
|
|
|
let out = Command::new(BIN)
|
|
.args(["run", bp_path.to_str().expect("utf-8 path")])
|
|
.current_dir(&cwd)
|
|
.output()
|
|
.expect("spawn aura run");
|
|
assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr));
|
|
|
|
// stdout is a MeasurementReport: the declared tap name, no R `metrics`, the
|
|
// measurement broker sentinel.
|
|
let report: serde_json::Value =
|
|
serde_json::from_slice(&out.stdout).expect("stdout parses as MeasurementReport JSON");
|
|
assert_eq!(report["taps"], serde_json::json!(["fast_tap"]));
|
|
assert!(report.get("metrics").is_none(), "a measurement run has no R metrics");
|
|
assert_eq!(report["manifest"]["broker"], "measurement");
|
|
|
|
// The tapped series is persisted through the trace store.
|
|
let trace_path = cwd.join("runs/traces/sma_signal/fast_tap.json");
|
|
let trace_text = std::fs::read_to_string(&trace_path)
|
|
.unwrap_or_else(|e| panic!("expected a persisted tap trace at {}: {e}", trace_path.display()));
|
|
let trace: serde_json::Value = serde_json::from_str(&trace_text).expect("parse tap trace json");
|
|
assert_eq!(trace["tap"], "fast_tap");
|
|
assert_eq!(trace["kinds"], serde_json::json!(["F64"]));
|
|
}
|
|
|
|
/// #310: the measurement arm honours the `--tap` selector — a `last`
|
|
/// fold lands one summary row instead of the full series.
|
|
#[test]
|
|
fn measurement_run_honours_the_tap_selector() {
|
|
let cwd = temp_cwd("selector-last");
|
|
let bp_path = cwd.join("measurement.json");
|
|
std::fs::write(&bp_path, measurement_blueprint_json()).expect("write measurement blueprint");
|
|
let out = Command::new(BIN)
|
|
.args(["run", bp_path.to_str().expect("utf-8 path"), "--tap", "fast_tap=last"])
|
|
.current_dir(&cwd)
|
|
.output()
|
|
.expect("spawn aura run");
|
|
assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr));
|
|
|
|
let trace_text = std::fs::read_to_string(cwd.join("runs/traces/sma_signal/fast_tap.json"))
|
|
.expect("read fold trace");
|
|
let trace: serde_json::Value = serde_json::from_str(&trace_text).expect("parse fold trace");
|
|
let rows = trace["columns"][0].as_array().expect("columns[0] array");
|
|
assert_eq!(rows.len(), 1, "a fold lands exactly one summary row");
|
|
// last warm SMA(2) value over the synthetic stream = (1.0097 + 1.0092) / 2
|
|
let got = rows[0].as_f64().expect("f64 row");
|
|
let expected = (1.0097_f64 + 1.0092) / 2.0;
|
|
assert!((got - expected).abs() < 1e-9, "last row: got {got}, expected {expected}");
|
|
}
|