feat(aura-cli)!: retire the research-verb sugar — run/sweep/walkforward/mc/generalize removed
Slice 6 of the #319 retirement, the destructive half. The five flag verbs, their five Args structs and dispatch arms, the whole argv->document translator (verb_sugar.rs, 1257 lines) and 33 quintet-only helpers are gone; retired verbs now refuse at the clap layer as unknown commands. main.rs shrinks 4445 -> 2764 lines. The gated-intake route list narrows to the canonical layer + exec's blueprint leg; exec's own refusal prose no longer names dead verbs; test seeding recipes ride graph register. cli_run.rs walked the full disposition table: 94 tests retired with their dead flag surface (each with a named green twin where the property survives), 75 ported — exec ports keep assertions byte-identical, campaign ports inherit their golden grades unchanged (walkforward's 90/30/30 real roller and the archive window clipping the sugar applied silently are now explicit document fields — the equivalence held exactly, one reconstructed walkforward pooled expectancy_r within 1e-12, cause documented inline). Surface deltas recorded by the ports: per-cell refusals are #272 contained faults (exit 3 + warning) rather than hard exit 1; the synthetic per-seed mc family and generalize's merged cross-instrument family record were sugar-only constructs and retire with it (the campaign record's generalizations[] carries the data). refs #319
This commit is contained in:
@@ -108,17 +108,13 @@ struct CampaignRunLine<'a> {
|
||||
campaign_run: &'a CampaignRunRecord,
|
||||
}
|
||||
|
||||
/// Stdout shape of a campaign run. `Full` is `aura campaign run` (emit-gated
|
||||
/// member/selection lines + the always-on final `campaign_run` record line).
|
||||
/// `MemberLinesOnly` is dissolved-verb sugar: the generated document's
|
||||
/// `emit: ["family_table"]` already limits emission to member lines; the mode
|
||||
/// additionally suppresses the final record line so the verb's stdout
|
||||
/// contract is reproduced byte-for-byte. The record append is identical in
|
||||
/// both modes — presentation changes, the record does not.
|
||||
/// Stdout shape of a campaign run: emit-gated member/selection lines + the
|
||||
/// always-on final `campaign_run` record line. `MemberLinesOnly` (the
|
||||
/// dissolved-verb sugar path's suppressed-record-line mode) retired with the
|
||||
/// quintet (#319 Task 8) — `Full` is the only surviving shape.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub(crate) enum RunPresentation {
|
||||
Full,
|
||||
MemberLinesOnly,
|
||||
}
|
||||
|
||||
/// `aura campaign run <target>`: resolve, gate, execute, emit. Every `Err`
|
||||
@@ -184,12 +180,9 @@ pub(crate) struct CampaignRun {
|
||||
/// The one campaign executor path from a resolved content id onward: fetch
|
||||
/// the stored canonical bytes by id (so file addressing and id addressing
|
||||
/// produce the same realization by construction) and re-run the intrinsic
|
||||
/// tier on them, execute, and emit per `presentation`. Shared by
|
||||
/// `run_campaign` (`RunPresentation::Full`) and the dissolved-verb sugar path
|
||||
/// (`verb_sugar::run_sweep_sugar`, `RunPresentation::MemberLinesOnly`) — no
|
||||
/// project gate here: the sugar path must work project-less exactly as the
|
||||
/// inline verb it replaces did (store mechanics run against `env.registry()`
|
||||
/// in both cases).
|
||||
/// tier on them, execute, and emit per `presentation`. Called by
|
||||
/// `run_campaign` (`RunPresentation::Full`, the sole surviving shape since
|
||||
/// the quintet's dissolved-verb sugar path retired, #319 Task 8).
|
||||
pub(crate) fn run_campaign_by_id(
|
||||
campaign_id: &str,
|
||||
env: &Env,
|
||||
|
||||
@@ -32,11 +32,11 @@ fn zero_trade_note_text(n_windows: usize) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// The #313 zero-trade note, shared by both walk-forward paths (the
|
||||
/// synthetic-blueprint path in `main.rs` and the `--real` sugar path in
|
||||
/// `verb_sugar.rs`) so the wording AND the condition live in exactly one
|
||||
/// place. Takes the per-window trade counts; a no-op unless there is at
|
||||
/// least one window and every one of them traded zero times.
|
||||
/// The #313 zero-trade note (#319 Task 5: migrated onto the campaign
|
||||
/// walk-forward leg in `campaign_run.rs`, the surviving executor path) so
|
||||
/// the wording AND the condition live in exactly one place. Takes the
|
||||
/// per-window trade counts; a no-op unless there is at least one window and
|
||||
/// every one of them traded zero times.
|
||||
pub(crate) fn note_zero_trade_windows(mut window_trades: impl ExactSizeIterator<Item = u64>) {
|
||||
let n_windows = window_trades.len();
|
||||
if n_windows > 0 && window_trades.all(|n| n == 0) {
|
||||
|
||||
@@ -805,41 +805,6 @@ pub(crate) fn unresolved_namespace_hint(e: &LoadError, env: &aura_runner::projec
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate a blueprint-slot document — the `sweep`/`walkforward`/`mc`
|
||||
/// loaded-blueprint branches' dispatch-boundary check (#210 c0110 fieldtest
|
||||
/// finding: the sweep slot used to print the raw `{e:?}` Debug leak). Single-
|
||||
/// sourced here so the three dual-grammar subcommands cannot diverge (#184's
|
||||
/// convention, extended). Two refusals, checked in order:
|
||||
///
|
||||
/// 1. The document parses as JSON but is an op-script ARRAY (#157), not a
|
||||
/// built blueprint envelope — refused with a targeted hint pointing at
|
||||
/// `aura graph build`, since `blueprint_from_json` would otherwise surface
|
||||
/// a confusing internal serde shape mismatch.
|
||||
/// 2. `blueprint_from_json` fails to load the envelope — phrased house-style
|
||||
/// via [`blueprint_load_prose`] (+ [`unresolved_namespace_hint`] where it
|
||||
/// applies), never the raw `LoadError` Debug form.
|
||||
///
|
||||
/// `Ok(())` means the document loaded cleanly; callers that only need the
|
||||
/// validation (not the `Composite`) re-parse `doc` themselves afterward
|
||||
/// (`blueprint_from_json` is cheap and infallible at that point).
|
||||
pub(crate) fn blueprint_slot_prose(doc: &str, env: &aura_runner::project::Env) -> Result<(), String> {
|
||||
if matches!(serde_json::from_str::<serde_json::Value>(doc), Ok(serde_json::Value::Array(_))) {
|
||||
return Err(
|
||||
"this is an op-script (an op array), not a built blueprint — run \
|
||||
'aura graph build' first"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
blueprint_from_json(doc, &|t| env.resolve(t)).map(|_| ()).map_err(|e| {
|
||||
let mut msg = blueprint_load_prose(&e);
|
||||
if let Some(hint) = unresolved_namespace_hint(&e, env) {
|
||||
msg.push_str(" — ");
|
||||
msg.push_str(&hint);
|
||||
}
|
||||
msg
|
||||
})
|
||||
}
|
||||
|
||||
/// #196: build a `Composite` from a document that is EITHER a #155 blueprint
|
||||
/// envelope (a JSON object: `format_version` + `blueprint`) OR a construction
|
||||
/// op-list (a JSON array) — shape-discriminated on the top-level JSON type,
|
||||
@@ -893,34 +858,22 @@ pub(crate) fn composite_from_any(text: &str, env: &aura_runner::project::Env) ->
|
||||
/// function — C29's "registered artifacts are never retroactively
|
||||
/// invalidated" stays intact.
|
||||
///
|
||||
/// **Deliberate exceptions — direct `gate_authored_root_name` callers
|
||||
/// (main.rs).** A handful of fresh-FILE intakes reach the same unsanitized
|
||||
/// `traces/<name>/` seam (or `put_blueprint` the loaded envelope straight
|
||||
/// into the registry) without going through this wrapper, because each
|
||||
/// **Deliberate exception — the one direct `gate_authored_root_name` caller
|
||||
/// (main.rs).** One fresh-FILE intake reaches the same unsanitized
|
||||
/// `traces/<name>/` seam without going through this wrapper, because it
|
||||
/// already parses the document its own way and only needs the shared root-
|
||||
/// name gate bolted on, not the shape-discrimination `composite_from_any`
|
||||
/// does:
|
||||
/// - `aura run <blueprint.json>` (`dispatch_run`): envelope-only grammar (no
|
||||
/// op-script fallback), feeds `signal.name()` into
|
||||
/// - `exec <blueprint.json>` (`exec_blueprint_leg`, #319): envelope-only
|
||||
/// grammar (no op-script fallback), feeds `signal.name()` into
|
||||
/// `run_signal_r`/`run_measurement` -> `bind_tap_plan` ->
|
||||
/// `TraceStore::begin_run`.
|
||||
/// - `validate_and_register_axes` (shared by `generalize`,
|
||||
/// `sweep --real`, `walkforward --real`, `mc --real`): `put_blueprint`s
|
||||
/// the loaded envelope by topology hash before any of those four verbs
|
||||
/// touches an archive.
|
||||
/// - `run_blueprint_sweep` / `run_blueprint_walkforward` / `run_blueprint_mc`
|
||||
/// (the synthetic, no-`--real` family builders): same `put_blueprint`
|
||||
/// reason, on the routes that bypass `validate_and_register_axes`
|
||||
/// entirely (#331 cycle-close — these used to plant an ungated envelope
|
||||
/// in the store; see each fn's own comment).
|
||||
/// - `list_blueprint_axes` (`aura sweep <FILE> --list-axes`, main.rs): no
|
||||
/// registry write and no trace directory here, but a shape-violating root
|
||||
/// name otherwise mangles through `wrapped_to_raw_axis` into a printed,
|
||||
/// non-bindable axis name instead of refusing (#331 fieldtest finding
|
||||
/// c331_2e) — the class rule is every CLI intake reading an authored
|
||||
/// envelope from a file gates the root name, not only the writing ones.
|
||||
///
|
||||
/// All of these share the identical `name_gate` + `name_gate_fault_prose`
|
||||
/// (The quintet's other direct callers — `dispatch_run`,
|
||||
/// `validate_and_register_axes`, `run_blueprint_sweep`/`_walkforward`/`_mc`,
|
||||
/// `list_blueprint_axes` — retired with the quintet, #319 Task 8.)
|
||||
///
|
||||
/// This shares the identical `name_gate` + `name_gate_fault_prose`
|
||||
/// primitives this wrapper uses, so the refusal wording is byte-identical
|
||||
/// across every authored-file-intake route even though the shape-
|
||||
/// discrimination step is not shared by them.
|
||||
@@ -934,10 +887,10 @@ pub(crate) fn composite_from_authored_text(
|
||||
}
|
||||
|
||||
/// The root-name shape gate itself (#331 delta re-review), factored out of
|
||||
/// [`composite_from_authored_text`] so `dispatch_run`'s narrower-grammar file
|
||||
/// intake (see that fn's doc comment) can share the exact `name_gate` call and
|
||||
/// `name_gate_fault_prose` wording without going through the shape-discriminating
|
||||
/// wrapper.
|
||||
/// [`composite_from_authored_text`] so `exec_blueprint_leg`'s narrower-grammar
|
||||
/// file intake (see that fn's doc comment) can share the exact `name_gate`
|
||||
/// call and `name_gate_fault_prose` wording without going through the
|
||||
/// shape-discriminating wrapper.
|
||||
pub(crate) fn gate_authored_root_name(name: &str) -> Result<(), String> {
|
||||
name_gate(name).map_err(|fault| name_gate_fault_prose(name, &fault))
|
||||
}
|
||||
|
||||
+43
-1724
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -40,36 +40,26 @@ const SWEEP_ONLY_PROCESS_DOC: &str = r#"{
|
||||
"pipeline": [ { "block": "std::sweep", "metric": "sqn_normalized", "select": "argmax" } ]
|
||||
}"#;
|
||||
|
||||
/// Seed one blueprint (its params bound, reopened by the sweep axes per
|
||||
/// #246) into the built demo project's store via a real sweep and return
|
||||
/// its content id — copied verbatim from `research_docs.rs`'s recipe of the
|
||||
/// same name.
|
||||
/// Seed one blueprint into the built demo project's store via `aura graph
|
||||
/// register` and return its content id — copied verbatim from
|
||||
/// `research_docs.rs`'s recipe of the same name (#319 — the retired `sweep`
|
||||
/// side-effect seeding this used to ride played no role in the returned
|
||||
/// content id, a pure function of the loaded blueprint's canonical bytes;
|
||||
/// `name` labels the registered id, mirroring the old per-call sweep family
|
||||
/// name).
|
||||
fn seed_blueprint(dir: &Path, name: &str) -> String {
|
||||
let closed_bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
let (out, code) = run_code_in(
|
||||
dir,
|
||||
&[
|
||||
"sweep",
|
||||
&closed_bp,
|
||||
"--axis",
|
||||
"fast.length=2,4",
|
||||
"--axis",
|
||||
"slow.length=8,16",
|
||||
"--name",
|
||||
name,
|
||||
],
|
||||
);
|
||||
assert_eq!(code, Some(0), "seed sweep failed: {out}");
|
||||
std::fs::read_dir(dir.join("runs").join("blueprints"))
|
||||
.expect("blueprints dir")
|
||||
let (out, code) = run_code_in(dir, &["graph", "register", &closed_bp, "--name", name]);
|
||||
assert_eq!(code, Some(0), "seed register failed: {out}");
|
||||
out.lines()
|
||||
.find(|l| l.starts_with("registered blueprint "))
|
||||
.expect("register line")
|
||||
.trim_start_matches("registered blueprint ")
|
||||
.split(' ')
|
||||
.next()
|
||||
.expect("one stored blueprint")
|
||||
.expect("dir entry")
|
||||
.path()
|
||||
.file_stem()
|
||||
.expect("stem")
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
.expect("id")
|
||||
.trim_start_matches("content:")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// Register `doc` as a process document in the project store; returns its
|
||||
|
||||
+2110
-4881
File diff suppressed because it is too large
Load Diff
+409
-52
@@ -61,36 +61,26 @@ const CAMPAIGN_DOC: &str = r#"{
|
||||
"presentation": { "persist_taps": [], "emit": ["family_table"] }
|
||||
}"#;
|
||||
|
||||
/// Seed one blueprint (its params bound, reopened by the sweep axes per
|
||||
/// #246) into the built demo project's store via a real sweep and return
|
||||
/// its content id — copied verbatim from `research_docs.rs`'s recipe of the
|
||||
/// same name.
|
||||
/// Seed one blueprint into the built demo project's store via `aura graph
|
||||
/// register` and return its content id — copied verbatim from
|
||||
/// `research_docs.rs`'s recipe of the same name (#319 — the retired `sweep`
|
||||
/// side-effect seeding this used to ride played no role in the returned
|
||||
/// content id, a pure function of the loaded blueprint's canonical bytes;
|
||||
/// `name` labels the registered id, mirroring the old per-call sweep family
|
||||
/// name).
|
||||
fn seed_blueprint(dir: &Path, name: &str) -> String {
|
||||
let closed_bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
let (out, code) = run_code_in(
|
||||
dir,
|
||||
&[
|
||||
"sweep",
|
||||
&closed_bp,
|
||||
"--axis",
|
||||
"fast.length=2,4",
|
||||
"--axis",
|
||||
"slow.length=8,16",
|
||||
"--name",
|
||||
name,
|
||||
],
|
||||
);
|
||||
assert_eq!(code, Some(0), "seed sweep failed: {out}");
|
||||
std::fs::read_dir(dir.join("runs").join("blueprints"))
|
||||
.expect("blueprints dir")
|
||||
let (out, code) = run_code_in(dir, &["graph", "register", &closed_bp, "--name", name]);
|
||||
assert_eq!(code, Some(0), "seed register failed: {out}");
|
||||
out.lines()
|
||||
.find(|l| l.starts_with("registered blueprint "))
|
||||
.expect("register line")
|
||||
.trim_start_matches("registered blueprint ")
|
||||
.split(' ')
|
||||
.next()
|
||||
.expect("one stored blueprint")
|
||||
.expect("dir entry")
|
||||
.path()
|
||||
.file_stem()
|
||||
.expect("stem")
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
.expect("id")
|
||||
.trim_start_matches("content:")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// Register `doc` as a process document in the project store; returns its
|
||||
@@ -285,17 +275,6 @@ fn measurement_blueprint_json() -> String {
|
||||
serde_json::to_string(&v).expect("re-serialize measurement blueprint")
|
||||
}
|
||||
|
||||
/// Blank the one volatile manifest field (`manifest.commit`, the compile-time
|
||||
/// build sha) before a byte comparison — the same blanking `aura-bench`'s
|
||||
/// `run_line_fingerprint` applies before hashing (`fixed_cost.rs:21-31`).
|
||||
fn strip_volatile(line: &str) -> String {
|
||||
let mut v: serde_json::Value = serde_json::from_str(line).expect("record line parses");
|
||||
if let Some(commit) = v.get_mut("manifest").and_then(|m| m.get_mut("commit")) {
|
||||
*commit = serde_json::Value::String(String::new());
|
||||
}
|
||||
serde_json::to_string(&v).expect("re-serializing a parsed Value cannot fail")
|
||||
}
|
||||
|
||||
/// Single run of a fully-bound blueprint on the synthetic stream: the record
|
||||
/// line is byte-shape-identical to `aura run`'s (manifest-first, one line) —
|
||||
/// mirrors `cli_run.rs::aura_run_loads_and_runs_a_blueprint_file`.
|
||||
@@ -310,20 +289,6 @@ fn exec_blueprint_file_emits_the_single_run_record_line() {
|
||||
assert!(v["manifest"].get("commit").is_some());
|
||||
}
|
||||
|
||||
/// Parity: `exec` and `run` emit the identical record line for the same
|
||||
/// file (both verbs alive until Task 8; the pin survives `run`'s removal by
|
||||
/// keeping only the exec half and its literal expectations).
|
||||
#[test]
|
||||
fn exec_blueprint_record_line_matches_runs_line_bytes() {
|
||||
let (run_out, run_code_) = run_code(&["run", "examples/r_sma.json"]);
|
||||
let (exec_out, exec_code) = run_code(&["exec", "examples/r_sma.json"]);
|
||||
assert_eq!(run_code_, Some(0), "stdout/stderr: {run_out}");
|
||||
assert_eq!(exec_code, Some(0), "stdout/stderr: {exec_out}");
|
||||
let run_line = run_out.lines().find(|l| l.starts_with('{')).expect("run record line");
|
||||
let exec_line = exec_out.lines().find(|l| l.starts_with('{')).expect("exec record line");
|
||||
assert_eq!(strip_volatile(run_line), strip_volatile(exec_line));
|
||||
}
|
||||
|
||||
/// The migrated root-name gate (retirement inventory): exec's blueprint
|
||||
/// intake refuses a bad root name with `run`'s exact prose, before any
|
||||
/// trace write — mirrors
|
||||
@@ -715,3 +680,395 @@ fn exec_campaign_walkforward_with_trades_emits_no_zero_trade_note() {
|
||||
assert_eq!(code, Some(0), "stdout/stderr: {out}");
|
||||
assert!(!out.contains("recorded zero trades"), "a traded run emits no zero-trade note: {out}");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Task 8: `cli_run.rs` disposition — ports onto `exec` (retire+port half only;
|
||||
// the port-to-campaign-document half is a separate follow-up dispatch).
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Property (#249, ported from `cli_run.rs::run_manifest_stamps_untouched_bound_defaults`):
|
||||
/// a plain `exec` of a fully bound blueprint records the untouched bound param
|
||||
/// values directly in the manifest — `params` stays `[]` (nothing varied) and
|
||||
/// every bound value lands in `defaults`, RAW (#328) and ordered by
|
||||
/// `bound_param_space()` (fast, slow, bias — node-declaration order).
|
||||
#[test]
|
||||
fn exec_manifest_stamps_untouched_bound_defaults() {
|
||||
let (out, code) = run_code(&["exec", "examples/r_sma.json"]);
|
||||
assert_eq!(code, Some(0), "stdout/stderr: {out}");
|
||||
assert!(out.contains("\"params\":[]"), "params stays empty on a plain exec: {out}");
|
||||
assert!(
|
||||
out.contains(
|
||||
r#""defaults":[["fast.length",{"I64":2}],["slow.length",{"I64":4}],["bias.scale",{"F64":0.5}]]"#
|
||||
),
|
||||
"manifest must carry the untouched bound defaults: {out}",
|
||||
);
|
||||
}
|
||||
|
||||
/// Property (ported from `cli_run.rs::run_manifest_commit_carries_real_git_head`):
|
||||
/// the RunManifest is self-identifying — its `commit` carries the real git
|
||||
/// HEAD that produced the run, never the `"unknown"` placeholder. Structural
|
||||
/// (contains, not equals) since the working tree may be dirty.
|
||||
#[test]
|
||||
fn exec_manifest_commit_carries_real_git_head() {
|
||||
let head = std::process::Command::new("git")
|
||||
.args(["rev-parse", "HEAD"])
|
||||
.output()
|
||||
.expect("spawn git rev-parse HEAD");
|
||||
assert!(head.status.success(), "git rev-parse HEAD failed");
|
||||
let head_sha = String::from_utf8(head.stdout).expect("utf-8 sha");
|
||||
let head_sha = head_sha.trim();
|
||||
assert!(!head_sha.is_empty(), "empty HEAD sha");
|
||||
|
||||
let (out, code) = run_code(&["exec", "examples/r_sma.json"]);
|
||||
assert_eq!(code, Some(0), "stdout/stderr: {out}");
|
||||
let key = "\"commit\":\"";
|
||||
let start = out.find(key).expect("manifest has a commit field") + key.len();
|
||||
let rest = &out[start..];
|
||||
let end = rest.find('"').expect("commit field is a closed string");
|
||||
let commit = &rest[..end];
|
||||
assert_ne!(commit, "unknown", "manifest.commit is still the placeholder: {out}");
|
||||
assert!(
|
||||
commit.contains(head_sha),
|
||||
"manifest.commit {commit:?} should contain the current HEAD sha {head_sha:?}: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
/// Property (#275, ported from `cli_run.rs::run_prints_json_and_exits_zero` — a
|
||||
/// disposition-table gap: the plain synthetic single-run report shape was never
|
||||
/// re-pinned onto `exec` although its property, a synthetic blueprint-leg
|
||||
/// single run, belongs squarely to this leg): the canonical record-line shape
|
||||
/// — nested manifest + metrics, stable keys, exactly one stdout line.
|
||||
#[test]
|
||||
fn exec_prints_json_and_exits_zero() {
|
||||
let (out, code) = run_code(&["exec", "examples/r_sma.json"]);
|
||||
assert_eq!(code, Some(0), "stdout/stderr: {out}");
|
||||
assert_eq!(out.lines().count(), 1, "stdout was: {out:?}");
|
||||
let line = out.trim_end();
|
||||
assert!(line.starts_with("{\"manifest\":{\"commit\":\""), "got: {line}");
|
||||
assert!(line.contains("\"broker\":\"sim-optimal+risk-executor(pip_size=0.0001)\""), "got: {line}");
|
||||
assert!(line.contains("\"window\":[1,18]"), "got: {line}");
|
||||
assert!(line.contains("\"metrics\":{\"total_pips\":"), "got: {line}");
|
||||
assert!(line.contains("\"r\":{\"expectancy_r\":"), "got: {line}");
|
||||
}
|
||||
|
||||
/// A blueprint referencing a node type outside the closed vocabulary fails
|
||||
/// clean at load (invariant 9), in house-style prose (mirrors
|
||||
/// `cli_run.rs::aura_run_rejects_an_unknown_node_blueprint`).
|
||||
#[test]
|
||||
fn exec_rejects_an_unknown_node_blueprint() {
|
||||
let (out, code) = run_code(&["exec", "tests/fixtures/unknown_node.json"]);
|
||||
assert_ne!(code, Some(0), "an unknown node type must fail the run: {out}");
|
||||
assert!(out.contains(r#"unknown node type "Nope""#), "house-style prose names the type: {out}");
|
||||
assert!(!out.contains("UnknownNodeType"), "does not leak the Debug variant name: {out}");
|
||||
}
|
||||
|
||||
/// Property (#231 task 4): `exec`'s blueprint leg has no `--real` at all
|
||||
/// (invariant 7 — a real-data single run is a one-cell campaign document), so
|
||||
/// a multi-column blueprint always hits the synthetic-only refusal — mirrors
|
||||
/// `cli_run.rs::run_synthetic_refuses_a_multi_column_blueprint` over the
|
||||
/// shipped `examples/r_channel.json`.
|
||||
#[test]
|
||||
fn exec_refuses_a_multi_column_blueprint() {
|
||||
let (out, code) = run_code(&["exec", "examples/r_channel.json"]);
|
||||
assert_eq!(code, Some(1), "stdout/stderr: {out}");
|
||||
assert!(out.contains("consumes columns beyond close"), "names the shape: {out}");
|
||||
assert!(out.contains("--real"), "names the remedy: {out}");
|
||||
}
|
||||
|
||||
/// A CLOSED high/low blueprint (zero free knobs — copied verbatim from
|
||||
/// `cli_run.rs::HL_RANGE_CLOSED_BLUEPRINT`) drives the exact-prose twin of the
|
||||
/// refusal above, data-free (the guard fires directly after binding
|
||||
/// resolution, before any archive access) — mirrors
|
||||
/// `cli_run.rs::run_synthetic_refuses_a_multi_column_blueprint_before_data_access`.
|
||||
const HL_RANGE_CLOSED_BLUEPRINT: &str = r#"{
|
||||
"format_version": 1,
|
||||
"blueprint": {
|
||||
"name": "hl_range",
|
||||
"nodes": [ {"primitive":{"type":"Sub"}} ],
|
||||
"edges": [],
|
||||
"input_roles": [
|
||||
{"name":"high","targets":[{"node":0,"slot":0}],"source":"F64"},
|
||||
{"name":"low","targets":[{"node":0,"slot":1}],"source":"F64"}
|
||||
],
|
||||
"output": [{"node":0,"field":0,"name":"bias"}]
|
||||
}
|
||||
}"#;
|
||||
|
||||
#[test]
|
||||
fn exec_refuses_a_multi_column_blueprint_before_data_access() {
|
||||
let cwd = temp_cwd("exec_multicolumn_refusal");
|
||||
let bp_path = cwd.join("hl_range.json");
|
||||
std::fs::write(&bp_path, HL_RANGE_CLOSED_BLUEPRINT).expect("write fixture");
|
||||
let (out, code) = run_code_in(&cwd, &["exec", bp_path.to_str().expect("utf-8 path")]);
|
||||
assert_eq!(code, Some(1), "stdout/stderr: {out}");
|
||||
assert_eq!(
|
||||
out.trim_end(),
|
||||
"aura: strategy \"hl_range\" consumes columns beyond close (high, low) — synthetic \
|
||||
data generates a close series only; run with --real <SYMBOL>"
|
||||
);
|
||||
}
|
||||
|
||||
/// Property (#210, dissolution — content-addressed idempotency), ported onto
|
||||
/// `exec`'s campaign FILE leg (the register-then-run sugar,
|
||||
/// `campaign_run.rs::run_campaign`'s target resolution): re-`exec`ing the
|
||||
/// IDENTICAL campaign document file does not litter the store with a second
|
||||
/// registered campaign document — `put_campaign` writes under a content-id
|
||||
/// filename, so two runs of the same file collapse onto one registered
|
||||
/// document while each invocation still records its own `campaign_runs.jsonl`
|
||||
/// line (a run is an event, a document is content) — mirrors
|
||||
/// `cli_run.rs::generalize_repeated_identical_invocation_does_not_litter_the_store`,
|
||||
/// whose translator generated the document; here the document is authored and
|
||||
/// `exec` performs only the file leg's own register-then-run idempotency.
|
||||
#[test]
|
||||
fn exec_campaign_file_repeated_identical_invocation_does_not_litter_the_store() {
|
||||
let (dir, _fixture) = fresh_project_with_data();
|
||||
let runs_dir = dir.join("runs");
|
||||
std::fs::remove_dir_all(&runs_dir).ok();
|
||||
let _cleanup = ScratchGuard(vec![
|
||||
ScratchPath::Dir(runs_dir.clone()),
|
||||
ScratchPath::File(dir.join("idem.process.json")),
|
||||
ScratchPath::File(dir.join("idem.campaign.json")),
|
||||
]);
|
||||
let bp_id = seed_blueprint(&dir, "exec-idempotence-seed");
|
||||
let proc_id = register_process_doc(&dir, "idem.process.json", SWEEP_ONLY_PROCESS_DOC);
|
||||
let doc = campaign_doc_json_for("SYMA", &bp_id, &proc_id, (1709251200000, 1719791999999));
|
||||
write_doc(&dir, "idem.campaign.json", &doc);
|
||||
|
||||
let (first_out, first_code) = run_code_in(&dir, &["exec", "idem.campaign.json"]);
|
||||
assert_eq!(first_code, Some(0), "first run: {first_out}");
|
||||
let (second_out, second_code) = run_code_in(&dir, &["exec", "idem.campaign.json"]);
|
||||
assert_eq!(second_code, Some(0), "second run: {second_out}");
|
||||
|
||||
let campaigns_count = std::fs::read_dir(dir.join("runs").join("campaigns"))
|
||||
.map(|d| d.count())
|
||||
.unwrap_or(0);
|
||||
assert_eq!(campaigns_count, 1, "two identical file-target invocations register one campaign document");
|
||||
let runs_log = std::fs::read_to_string(dir.join("runs").join("campaign_runs.jsonl"))
|
||||
.expect("campaign_runs.jsonl exists");
|
||||
assert_eq!(runs_log.lines().count(), 2, "each invocation still records its own campaign run");
|
||||
}
|
||||
|
||||
/// `campaign_doc_json_for`'s shape with the axis grid parameterized — needed
|
||||
/// so the distinctness twin below can vary campaign content without touching
|
||||
/// the window (keeping the known-good data-availability window fixed).
|
||||
fn campaign_doc_json_for_axis_grid(
|
||||
instrument: &str,
|
||||
bp_id: &str,
|
||||
proc_id: &str,
|
||||
window: (i64, i64),
|
||||
fast_values: &str,
|
||||
slow_values: &str,
|
||||
) -> String {
|
||||
format!(
|
||||
r#"{{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "run-seam-grid",
|
||||
"data": {{ "instruments": ["{instrument}"], "windows": [ {{ "from_ms": {from}, "to_ms": {to} }} ] }},
|
||||
"strategies": [ {{ "ref": {{ "content_id": "{bp_id}" }},
|
||||
"axes": {{ "fast.length": {{ "kind": "I64", "values": [{fast_values}] }},
|
||||
"slow.length": {{ "kind": "I64", "values": [{slow_values}] }} }} }} ],
|
||||
"process": {{ "ref": {{ "content_id": "{proc_id}" }} }},
|
||||
"seed": 7,
|
||||
"presentation": {{ "persist_taps": [], "emit": ["family_table"] }}
|
||||
}}"#,
|
||||
from = window.0,
|
||||
to = window.1,
|
||||
)
|
||||
}
|
||||
|
||||
/// Property (#210, dissolution — content-addressed distinctness), ported onto
|
||||
/// `exec`'s campaign FILE leg: two DIFFERENT campaign documents (differing
|
||||
/// axis-grid content, same window) persist as TWO distinct registered
|
||||
/// campaign documents — the twin of the idempotency test above, ruling out a
|
||||
/// store that always overwrites one fixed filename regardless of content —
|
||||
/// mirrors `cli_run.rs::generalize_distinct_invocations_persist_distinct_campaign_documents`.
|
||||
#[test]
|
||||
fn exec_campaign_file_distinct_invocations_persist_distinct_campaign_documents() {
|
||||
let (dir, _fixture) = fresh_project_with_data();
|
||||
let runs_dir = dir.join("runs");
|
||||
std::fs::remove_dir_all(&runs_dir).ok();
|
||||
let _cleanup = ScratchGuard(vec![
|
||||
ScratchPath::Dir(runs_dir.clone()),
|
||||
ScratchPath::File(dir.join("distinct.process.json")),
|
||||
ScratchPath::File(dir.join("distinct_a.campaign.json")),
|
||||
ScratchPath::File(dir.join("distinct_b.campaign.json")),
|
||||
]);
|
||||
let bp_id = seed_blueprint(&dir, "exec-distinct-seed");
|
||||
let proc_id = register_process_doc(&dir, "distinct.process.json", SWEEP_ONLY_PROCESS_DOC);
|
||||
let window = (1709251200000, 1719791999999);
|
||||
let doc_a = campaign_doc_json_for_axis_grid("SYMA", &bp_id, &proc_id, window, "2", "8");
|
||||
write_doc(&dir, "distinct_a.campaign.json", &doc_a);
|
||||
let (out_a, code_a) = run_code_in(&dir, &["exec", "distinct_a.campaign.json"]);
|
||||
assert_eq!(code_a, Some(0), "first document run: {out_a}");
|
||||
|
||||
let doc_b = campaign_doc_json_for_axis_grid("SYMA", &bp_id, &proc_id, window, "2, 4", "8, 16");
|
||||
write_doc(&dir, "distinct_b.campaign.json", &doc_b);
|
||||
let (out_b, code_b) = run_code_in(&dir, &["exec", "distinct_b.campaign.json"]);
|
||||
assert_eq!(code_b, Some(0), "second document run: {out_b}");
|
||||
|
||||
let campaigns_count = std::fs::read_dir(dir.join("runs").join("campaigns"))
|
||||
.map(|d| d.count())
|
||||
.unwrap_or(0);
|
||||
assert_eq!(campaigns_count, 2, "two distinct documents persist as two distinct campaign documents");
|
||||
}
|
||||
|
||||
/// Property (C22 first-contact ergonomics, #20), ported onto the surviving
|
||||
/// verb set (mirrors `cli_run.rs::help_flag_prints_usage_to_stdout_and_exits_zero`,
|
||||
/// whose "the usage names the only subcommand a newcomer can run" assertion
|
||||
/// named the now-retired `run` — the newcomer's reflex first command is
|
||||
/// `exec` today): `--help`/`-h` prints to stdout and exits 0; an unknown
|
||||
/// subcommand keeps the error path (exit 2).
|
||||
#[test]
|
||||
fn exec_help_flag_prints_usage_to_stdout_and_exits_zero() {
|
||||
for flag in ["--help", "-h"] {
|
||||
let (out, code) = run_code(&[flag]);
|
||||
assert_eq!(code, Some(0), "`aura {flag}` exit: {out}");
|
||||
assert!(!out.trim().is_empty(), "`aura {flag}` should print help to stdout: {out}");
|
||||
assert!(out.contains("exec"), "`aura {flag}` help should mention the `exec` subcommand: {out}");
|
||||
}
|
||||
let (out, code) = run_code(&["frobnicate"]);
|
||||
assert_eq!(code, Some(2), "unknown subcommand must stay exit 2: {out}");
|
||||
}
|
||||
|
||||
/// Property (post-clap migration, #175), ported onto the surviving verb set
|
||||
/// (mirrors `cli_run.rs::per_subcommand_help_is_scoped_stdout_exit_zero`,
|
||||
/// which enumerated the now-retired quintet): every surviving subcommand's
|
||||
/// `--help`/`-h` prints its own scoped Options section to stdout, exit 0,
|
||||
/// nothing on stderr.
|
||||
#[test]
|
||||
fn exec_per_subcommand_help_is_scoped_stdout_exit_zero() {
|
||||
for sub in ["exec", "chart", "graph", "runs", "reproduce", "new", "nodes", "process", "campaign", "data", "measure"] {
|
||||
for help in ["--help", "-h"] {
|
||||
let (out, code) = run_code(&[sub, help]);
|
||||
assert_eq!(code, Some(0), "{sub} {help} exit: {out}");
|
||||
assert!(!out.trim().is_empty(), "{sub} {help} stdout empty");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Property (mirrors `cli_run.rs::subcommand_help_is_scoped_not_uniform`): a
|
||||
/// subcommand's `--help` is NOT byte-identical to another's — each documents
|
||||
/// its own options, never one shared global blob.
|
||||
#[test]
|
||||
fn exec_subcommand_help_is_scoped_not_uniform() {
|
||||
let (exec_help, exec_code) = run_code(&["exec", "--help"]);
|
||||
let (graph_help, graph_code) = run_code(&["graph", "--help"]);
|
||||
assert_eq!(exec_code, Some(0), "exec --help exit");
|
||||
assert_eq!(graph_code, Some(0), "graph --help exit");
|
||||
assert!(exec_help.contains("--override"), "exec help names its flags: {exec_help:?}");
|
||||
assert_ne!(exec_help, graph_help, "per-subcommand help must be scoped, not uniform");
|
||||
}
|
||||
|
||||
/// The GNU `--flag=value` equals form is accepted (mirrors
|
||||
/// `cli_run.rs::gnu_equals_form_is_accepted`; the surviving vehicle flag is
|
||||
/// `--override`, since `--seed` retired with `run`'s built-in-harness
|
||||
/// grammar, invariant 7).
|
||||
#[test]
|
||||
fn exec_gnu_equals_form_is_accepted() {
|
||||
let (out, code) = run_code(&["exec", "examples/r_sma.json", "--override=fast.length=8"]);
|
||||
assert_eq!(code, Some(0), "stdout/stderr: {out}");
|
||||
}
|
||||
|
||||
/// The `--` end-of-options terminator is recognized (mirrors
|
||||
/// `cli_run.rs::double_dash_terminator_is_recognized`).
|
||||
#[test]
|
||||
fn exec_double_dash_terminator_is_recognized() {
|
||||
let (out, code) = run_code(&["exec", "examples/r_sma.json", "--override", "fast.length=8", "--"]);
|
||||
assert_eq!(code, Some(0), "stdout/stderr: {out}");
|
||||
}
|
||||
|
||||
/// GNU long-option abbreviation (mirrors
|
||||
/// `cli_run.rs::long_option_abbreviation_is_accepted`): an unambiguous prefix
|
||||
/// of a long flag is accepted (`--over` -> `--override`), via clap's root
|
||||
/// `infer_long_args`.
|
||||
#[test]
|
||||
fn exec_long_option_abbreviation_is_accepted() {
|
||||
let (out, code) = run_code(&["exec", "examples/r_sma.json", "--over", "fast.length=8"]);
|
||||
assert_eq!(code, Some(0), "stdout/stderr: {out}");
|
||||
}
|
||||
|
||||
/// exec's target classification, ported/merged from two retired dual-grammar
|
||||
/// guards (`cli_run.rs::dual_grammar_stray_positional_is_a_usage_error_not_swallowed`
|
||||
/// and the retired `run_with_trailing_token_is_strict_and_exits_two`'s negative
|
||||
/// half): `target` is exec's one REQUIRED positional, so clap always parses
|
||||
/// it — there is no more optional-positional-plus-hidden-default to guard
|
||||
/// against. A bare, non-`.json` stray token is instead classified at RUNTIME
|
||||
/// by the campaign leg's own target resolution, which refuses naming both
|
||||
/// readings it tried, never silently defaulting to anything.
|
||||
#[test]
|
||||
fn exec_stray_target_that_is_neither_a_file_nor_a_content_id_refuses() {
|
||||
let (dir, _fixture) = fresh_project();
|
||||
let (out, code) = run_code_in(&dir, &["exec", "bogus"]);
|
||||
assert_eq!(code, Some(1), "stdout/stderr: {out}");
|
||||
assert!(
|
||||
out.contains("'bogus' is neither a readable .json file nor a 64-hex content id"),
|
||||
"stdout/stderr: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
/// exec's discriminator keys on `is_file()`, not the `.json` suffix (mirrors
|
||||
/// `cli_run.rs::dual_grammar_discriminator_requires_an_existing_file_not_just_json_suffix`):
|
||||
/// a `.json`-suffixed name that does not exist on disk is NOT read as a
|
||||
/// blueprint (which would fail with a file-read error) — it falls through to
|
||||
/// the same neither-file-nor-content-id classification the bare stray token
|
||||
/// above gets. The positive `.json`-file path stays covered by
|
||||
/// `exec_blueprint_file_emits_the_single_run_record_line`.
|
||||
#[test]
|
||||
fn exec_json_suffixed_nonexistent_target_is_not_read_as_a_blueprint() {
|
||||
let (dir, _fixture) = fresh_project();
|
||||
let (out, code) = run_code_in(&dir, &["exec", "definitely-not-a-real-file.json"]);
|
||||
assert_eq!(code, Some(1), "stdout/stderr: {out}");
|
||||
assert!(
|
||||
out.contains("'definitely-not-a-real-file.json' is neither a readable .json file nor a 64-hex content id"),
|
||||
"stdout/stderr: {out}"
|
||||
);
|
||||
}
|
||||
|
||||
/// The usage/runtime exit-code partition (#175 iteration 2, deviation #8),
|
||||
/// ported onto exec's own grammar (mirrors
|
||||
/// `cli_run.rs::exit_codes_partition_usage_two_from_runtime_one`, whose
|
||||
/// runtime half rode `run --real NONEXISTENT` — a flag exec's blueprint leg
|
||||
/// no longer has, invariant 7): an unknown flag is a command-line error
|
||||
/// (exit 2); `--override` naming no param of the blueprint is exec's own
|
||||
/// well-formed-but-unresolvable runtime failure (exit 1).
|
||||
#[test]
|
||||
fn exec_exit_codes_partition_usage_two_from_runtime_one() {
|
||||
let (usage_out, usage_code) = run_code(&["exec", "examples/r_sma.json", "--bogus"]);
|
||||
assert_eq!(usage_code, Some(2), "unknown flag is a usage error -> 2: {usage_out}");
|
||||
let (runtime_out, runtime_code) =
|
||||
run_code(&["exec", "examples/r_sma.json", "--override", "nope.knob=1"]);
|
||||
assert_eq!(
|
||||
runtime_code, Some(1),
|
||||
"a well-formed command with an unresolvable override is a runtime failure -> 1: {runtime_out}"
|
||||
);
|
||||
}
|
||||
|
||||
/// `exec` on the shipped closed r-sma example reproduces the built-in r-sma
|
||||
/// grade (mirrors `cli_run.rs::shipped_r_sma_example_reproduces_the_builtin_grade`).
|
||||
#[test]
|
||||
fn exec_shipped_r_sma_example_reproduces_the_builtin_grade() {
|
||||
let (out, code) = run_code(&["exec", "examples/r_sma.json"]);
|
||||
assert_eq!(code, Some(0), "stdout/stderr: {out}");
|
||||
assert!(out.contains("\"expectancy_r\":1.2710005136982836"), "{out}");
|
||||
assert!(out.contains("\"sqn\":3.141496526818299"), "{out}");
|
||||
assert!(out.contains("\"total_pips\":0.34185000000002036"), "{out}");
|
||||
assert!(out.contains("\"n_trades\":3"), "{out}");
|
||||
}
|
||||
|
||||
/// `exec` on the shipped closed r-meanrev example runs end to end through the
|
||||
/// real, separately-linked `aura` binary (mirrors
|
||||
/// `cli_run.rs::shipped_r_meanrev_example_runs_end_to_end_via_aura_run`). The
|
||||
/// synthetic stream never crosses the band at this window/k, so the grade is
|
||||
/// genuinely all-zero (no trade) — still a meaningful pin: a regression that
|
||||
/// broke the `Scale` band's CLI wiring would surface as a non-zero exit or a
|
||||
/// load error here, not as a metrics drift.
|
||||
#[test]
|
||||
fn exec_shipped_r_meanrev_example_runs_end_to_end() {
|
||||
let (out, code) = run_code(&["exec", "examples/r_meanrev.json"]);
|
||||
assert_eq!(code, Some(0), "stdout/stderr: {out}");
|
||||
assert!(out.contains("\"topology_hash\":\""), "manifest carries the topology hash: {out}");
|
||||
assert!(out.contains("\"expectancy_r\":0.0"), "{out}");
|
||||
assert!(out.contains("\"n_trades\":0"), "{out}");
|
||||
assert!(out.contains("\"total_pips\":0.0"), "{out}");
|
||||
}
|
||||
|
||||
@@ -104,31 +104,91 @@ fn outside_a_project_the_demo_blueprint_is_unknown() {
|
||||
/// `<project-root>/runs/`, and no stray `runs/` must appear under the
|
||||
/// subdirectory. This is the property that makes `[paths]` project-relative
|
||||
/// rather than shell-relative (C17).
|
||||
///
|
||||
/// #319 vehicle note: the synthetic in-process family builder this test used
|
||||
/// to drive via bare `sweep --axis` (no `--real`) retired with the quintet —
|
||||
/// no surviving CLI surface produces a family record without touching a real
|
||||
/// archive (`exec`'s campaign leg always resolves against `DataServer`), so
|
||||
/// the vehicle is now a one-cell campaign over the `fresh_project_with_data`
|
||||
/// synthetic `SYMA` archive (mirrors `exec.rs`'s `seed_blueprint` +
|
||||
/// `campaign_doc_json_for` recipe), executed from a project subdirectory.
|
||||
#[test]
|
||||
fn project_registry_anchors_at_discovered_root_not_invocation_cwd() {
|
||||
let dir = built_project();
|
||||
let sub = dir.join("src");
|
||||
let (dir, _fixture) = common::fresh_project_with_data();
|
||||
let sub = dir.join("sub");
|
||||
std::fs::create_dir_all(&sub).expect("create invocation subdirectory");
|
||||
let runs_dir = dir.join("runs");
|
||||
std::fs::remove_dir_all(&runs_dir).ok();
|
||||
let closed_bp =
|
||||
format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
|
||||
let closed_bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
let reg_out = Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["graph", "register", &closed_bp, "--name", "proj-anchor-seed"])
|
||||
.current_dir(&dir)
|
||||
.output()
|
||||
.expect("spawn aura graph register");
|
||||
assert!(reg_out.status.success(), "register failed: {}", String::from_utf8_lossy(®_out.stderr));
|
||||
let reg_text = String::from_utf8_lossy(®_out.stdout);
|
||||
let bp_id = reg_text
|
||||
.lines()
|
||||
.find(|l| l.starts_with("registered blueprint "))
|
||||
.expect("register line")
|
||||
.trim_start_matches("registered blueprint ")
|
||||
.split(' ')
|
||||
.next()
|
||||
.expect("id")
|
||||
.to_string();
|
||||
|
||||
let process_doc = r#"{
|
||||
"format_version": 1,
|
||||
"kind": "process",
|
||||
"name": "sweep-only",
|
||||
"pipeline": [ { "block": "std::sweep", "metric": "sqn_normalized", "select": "argmax" } ]
|
||||
}"#;
|
||||
std::fs::write(dir.join("anchor.process.json"), process_doc).expect("write process doc");
|
||||
let proc_out = Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args(["process", "register", "anchor.process.json"])
|
||||
.current_dir(&dir)
|
||||
.output()
|
||||
.expect("spawn aura process register");
|
||||
assert!(proc_out.status.success(), "process register failed: {}", String::from_utf8_lossy(&proc_out.stderr));
|
||||
let proc_text = String::from_utf8_lossy(&proc_out.stdout);
|
||||
let proc_id = proc_text
|
||||
.lines()
|
||||
.find(|l| l.starts_with("registered process "))
|
||||
.expect("register line")
|
||||
.trim_start_matches("registered process ")
|
||||
.split(' ')
|
||||
.next()
|
||||
.expect("id")
|
||||
.trim_start_matches("content:")
|
||||
.to_string();
|
||||
|
||||
// The established `SYMA` window (2024-03..06, fully inside its 2024-01..08
|
||||
// span) other #319 vehicle campaigns use.
|
||||
let campaign_doc = format!(
|
||||
r#"{{
|
||||
"format_version": 1,
|
||||
"kind": "campaign",
|
||||
"name": "run-seam",
|
||||
"data": {{ "instruments": ["SYMA"], "windows": [ {{ "from_ms": 1709251200000, "to_ms": 1719791999999 }} ] }},
|
||||
"strategies": [ {{ "ref": {{ "content_id": "{bp_id}" }},
|
||||
"axes": {{ "fast.length": {{ "kind": "I64", "values": [2, 4] }},
|
||||
"slow.length": {{ "kind": "I64", "values": [8, 16] }} }} }} ],
|
||||
"process": {{ "ref": {{ "content_id": "{proc_id}" }} }},
|
||||
"seed": 7,
|
||||
"presentation": {{ "persist_taps": [], "emit": ["family_table"] }}
|
||||
}}"#
|
||||
);
|
||||
std::fs::write(dir.join("anchor.campaign.json"), &campaign_doc).expect("write campaign doc");
|
||||
|
||||
let out = Command::new(env!("CARGO_BIN_EXE_aura"))
|
||||
.args([
|
||||
"sweep",
|
||||
&closed_bp,
|
||||
"--axis",
|
||||
"fast.length=2,4",
|
||||
"--axis",
|
||||
"slow.length=8,16",
|
||||
"--name",
|
||||
"proj-anchor",
|
||||
])
|
||||
.args(["exec", "../anchor.campaign.json"])
|
||||
.current_dir(&sub)
|
||||
.output()
|
||||
.expect("spawn aura sweep");
|
||||
.expect("spawn aura exec");
|
||||
assert!(
|
||||
out.status.success(),
|
||||
"sweep failed: {}",
|
||||
"exec failed: {}",
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
);
|
||||
assert!(
|
||||
|
||||
@@ -319,34 +319,9 @@ fn campaign_validate_in_project_reports_referential_tier_end_to_end() {
|
||||
]);
|
||||
|
||||
// Seed one real, content-addressed blueprint into the project's own
|
||||
// store via a real sweep over its bound params, which the axes reopen
|
||||
// as overridable defaults (#246) (mirrors `project_load.rs`'s anchor
|
||||
// test).
|
||||
let closed_bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
let (sweep_out, sweep_code) = run_code_in(
|
||||
&dir,
|
||||
&[
|
||||
"sweep",
|
||||
&closed_bp,
|
||||
"--axis",
|
||||
"fast.length=2,4",
|
||||
"--axis",
|
||||
"slow.length=8,16",
|
||||
"--name",
|
||||
"campaign-ref-seed",
|
||||
],
|
||||
);
|
||||
assert_eq!(sweep_code, Some(0), "seed sweep failed: {sweep_out}");
|
||||
let bp_id = std::fs::read_dir(runs_dir.join("blueprints"))
|
||||
.expect("blueprints dir")
|
||||
.next()
|
||||
.expect("one stored blueprint")
|
||||
.expect("dir entry")
|
||||
.path()
|
||||
.file_stem()
|
||||
.expect("stem")
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
// store via `aura graph register` (#319 — the retired `sweep`
|
||||
// side-effect seeding this used to ride).
|
||||
let bp_id = seed_blueprint(&dir, "campaign-ref-seed");
|
||||
|
||||
// Register a valid AND executable process into the same project store:
|
||||
// this test's OK campaign must pass all three validate tiers, and
|
||||
@@ -441,32 +416,8 @@ fn campaign_validate_resolves_identity_ref_via_index_first_lookup_then_index_hit
|
||||
]);
|
||||
|
||||
// Seed one real, content-addressed blueprint (mirrors the content_id
|
||||
// sibling test above).
|
||||
let closed_bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
let (sweep_out, sweep_code) = run_code_in(
|
||||
&dir,
|
||||
&[
|
||||
"sweep",
|
||||
&closed_bp,
|
||||
"--axis",
|
||||
"fast.length=2,4",
|
||||
"--axis",
|
||||
"slow.length=8,16",
|
||||
"--name",
|
||||
"campaign-identity-seed",
|
||||
],
|
||||
);
|
||||
assert_eq!(sweep_code, Some(0), "seed sweep failed: {sweep_out}");
|
||||
let bp_id = std::fs::read_dir(runs_dir.join("blueprints"))
|
||||
.expect("blueprints dir")
|
||||
.next()
|
||||
.expect("one stored blueprint")
|
||||
.expect("dir entry")
|
||||
.path()
|
||||
.file_stem()
|
||||
.expect("stem")
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
// sibling test above; #319 — the retired `sweep` side-effect seeding).
|
||||
let bp_id = seed_blueprint(&dir, "campaign-identity-seed");
|
||||
let bp_path = runs_dir.join("blueprints").join(format!("{bp_id}.json"));
|
||||
|
||||
// The identity id of the SAME stored bytes — `--content-id FILE
|
||||
@@ -1278,35 +1229,26 @@ fn campaign_register_refuses_invalid_cost_section_and_writes_nothing() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Seed one blueprint (its params bound, reopened by the sweep axes per
|
||||
/// #246) into the built demo project's store via a real sweep and return
|
||||
/// its content id (the referential test's recipe).
|
||||
/// Seed one blueprint into the built demo project's store via `aura graph
|
||||
/// register` and return its content id (the referential test's recipe).
|
||||
/// `#319` retired the `sweep`-side-effect seeding this used to ride (its
|
||||
/// `--axis`/reopen mechanics played no role in the returned content id,
|
||||
/// which is a pure function of the loaded blueprint's canonical bytes);
|
||||
/// `name` labels the registered id for readability/uniqueness across call
|
||||
/// sites, mirroring the old per-call sweep family name.
|
||||
fn seed_blueprint(dir: &Path, name: &str) -> String {
|
||||
let closed_bp = format!("{}/examples/r_sma.json", env!("CARGO_MANIFEST_DIR"));
|
||||
let (out, code) = run_code_in(
|
||||
dir,
|
||||
&[
|
||||
"sweep",
|
||||
&closed_bp,
|
||||
"--axis",
|
||||
"fast.length=2,4",
|
||||
"--axis",
|
||||
"slow.length=8,16",
|
||||
"--name",
|
||||
name,
|
||||
],
|
||||
);
|
||||
assert_eq!(code, Some(0), "seed sweep failed: {out}");
|
||||
std::fs::read_dir(dir.join("runs").join("blueprints"))
|
||||
.expect("blueprints dir")
|
||||
let (out, code) = run_code_in(dir, &["graph", "register", &closed_bp, "--name", name]);
|
||||
assert_eq!(code, Some(0), "seed register failed: {out}");
|
||||
out.lines()
|
||||
.find(|l| l.starts_with("registered blueprint "))
|
||||
.expect("register line")
|
||||
.trim_start_matches("registered blueprint ")
|
||||
.split(' ')
|
||||
.next()
|
||||
.expect("one stored blueprint")
|
||||
.expect("dir entry")
|
||||
.path()
|
||||
.file_stem()
|
||||
.expect("stem")
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
.expect("id")
|
||||
.trim_start_matches("content:")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// Register `doc` as a process document in the project store; returns its id.
|
||||
|
||||
Reference in New Issue
Block a user