refactor: split the aura-std roster into C28 layer crates

Phase 4 of the Stratification milestone. aura-std held four C28 ladder
layers in one roster; this cuts them into layer-aligned, aura-core-only
node crates so the import direction is enforced by the crate graph:

- aura-std        — engine nodes only (arithmetic/logic/rolling + sinks)
- aura-market     — session, resample
- aura-strategy   — bias, stops, sizer, cost-model machinery
- aura-backtest   — sim_broker, position_management
- aura-vocabulary — the relocated closed std_vocabulary roster

Node modules move verbatim (byte-identical renames); consumers are
rewired by import path only. A new structural test
(aura-vocabulary/tests/c28_layering.rs) asserts each node crate's
[dependencies] stay within its C28-permitted inner set, catching the
acyclic-but-outward violation the compiler misses.

Behaviour byte-identical: full workspace suite green (1448 tests), no
golden edited, clippy -D warnings clean. C28 Status block updated.

closes #288
This commit is contained in:
2026-07-19 20:28:20 +02:00
parent 34ff539143
commit b39fd63396
66 changed files with 398 additions and 152 deletions
+11 -5
View File
@@ -1384,7 +1384,9 @@ mod tests {
use crate::test_fixtures::{composite_sma_cross_harness, synthetic_prices};
use crate::{f64_field, summarize, ParamRange, RandomSpace, RunManifest, VecSource};
use aura_core::{Cell, Ctx, FieldSpec, Firing, NodeSchema, Timestamp};
use aura_std::{Bias, Ema, LinComb, Recorder, SimBroker, Sma, Sub};
use aura_backtest::SimBroker;
use aura_std::{Ema, LinComb, Recorder, Sma, Sub};
use aura_strategy::Bias;
use std::sync::mpsc;
/// One knob fanning into two sibling open params passes the gate; the
@@ -3122,7 +3124,8 @@ mod tests {
#[test]
fn param_space_reflects_only_open_knobs() {
use aura_std::{Bias, Sma};
use aura_strategy::Bias;
use aura_std::Sma;
// "sma2_entry": Sma "bias" with length bound to 2 (a structural constant)
// plus Bias "exp" whose `scale` stays open. The bound knob must be
// absent from param_space; only the open one remains.
@@ -3148,7 +3151,8 @@ mod tests {
/// order — the bound value becomes a default an axis may override.
#[test]
fn reopen_returns_a_bound_knob_to_param_space() {
use aura_std::{Bias, Sma};
use aura_strategy::Bias;
use aura_std::Sma;
let strat = Composite::new(
"sma2_entry",
vec![
@@ -3169,7 +3173,8 @@ mod tests {
/// error, not a silent no-op — open axes pass through without reopen.
#[test]
fn reopen_unknown_or_open_path_errors() {
use aura_std::{Bias, Sma};
use aura_strategy::Bias;
use aura_std::Sma;
let strat = Composite::new(
"sma2_entry",
vec![
@@ -3190,7 +3195,8 @@ mod tests {
/// carries the bound value (the default the CLI renders in --list-axes).
#[test]
fn bound_param_space_is_path_qualified_with_values() {
use aura_std::{Bias, Sma};
use aura_strategy::Bias;
use aura_std::Sma;
let strat = Composite::new(
"sma2_entry",
vec![
+14 -13
View File
@@ -244,7 +244,7 @@ fn reconstruct(
}
/// Load a blueprint from canonical JSON, resolving each primitive's type identity
/// through the injected `resolve` (e.g. `aura_std::std_vocabulary`). The version
/// through the injected `resolve` (e.g. `aura_vocabulary::std_vocabulary`). The version
/// envelope is checked before the payload is reconstructed.
pub fn blueprint_from_json(
data: &str,
@@ -267,7 +267,8 @@ mod tests {
use crate::harness::{Edge, Target};
use crate::VecSource; // crate-root re-export (blueprint.rs tests import it the same way, e.g. :923)
use aura_core::{Scalar, ScalarKind, Timestamp};
use aura_std::{Bias, Recorder, Sma, Sub};
use aura_std::{Recorder, Sma, Sub};
use aura_strategy::Bias;
use std::sync::mpsc;
// Open param point for the sink-free signal (fast.length is bound): [slow.length, bias.scale].
@@ -303,7 +304,7 @@ mod tests {
fn serialized_blueprint_runs_bit_identical_to_rust_built() {
let rust_built = crate::test_fixtures::sink_free_sma_cross_signal();
let json = blueprint_to_json(&rust_built).expect("serializes");
let loaded = blueprint_from_json(&json, &|t| aura_std::std_vocabulary(t)).expect("loads");
let loaded = blueprint_from_json(&json, &|t| aura_vocabulary::std_vocabulary(t)).expect("loads");
let trace_rust = run_recording(rust_built);
let trace_loaded = run_recording(loaded);
@@ -316,7 +317,7 @@ mod tests {
fn serializer_and_loader_are_inverse() {
let original = crate::test_fixtures::sink_free_sma_cross_signal();
let json = blueprint_to_json(&original).expect("serializes");
let loaded = blueprint_from_json(&json, &|t| aura_std::std_vocabulary(t)).expect("loads");
let loaded = blueprint_from_json(&json, &|t| aura_vocabulary::std_vocabulary(t)).expect("loads");
// serialize -> load -> serialize is byte-stable (canonical round-trip identity)
assert_eq!(json, blueprint_to_json(&loaded).expect("re-serializes"));
}
@@ -339,7 +340,7 @@ mod tests {
vec![OutField { node: 0, field: 0, name: "bias".into() }],
);
let json = blueprint_to_json(&outer).expect("serializes");
let loaded = blueprint_from_json(&json, &|t| aura_std::std_vocabulary(t)).expect("loads");
let loaded = blueprint_from_json(&json, &|t| aura_vocabulary::std_vocabulary(t)).expect("loads");
assert_eq!(json, blueprint_to_json(&loaded).expect("re-serializes"));
// the nested composite survived as a composite, not flattened
assert!(json.contains(r#"{"composite":{"name":"sma_cross""#), "nested composite preserved");
@@ -370,7 +371,7 @@ mod tests {
assert_ne!(plain_json, doced_json, "the doc is canonical-byte-bearing");
assert!(doced_json.contains("why this graph"), "doc serialized: {doced_json}");
assert!(!plain_json.contains("\"doc\""), "absent doc emits no key: {plain_json}");
let loaded = blueprint_from_json(&doced_json, &|t| aura_std::std_vocabulary(t)).expect("loads");
let loaded = blueprint_from_json(&doced_json, &|t| aura_vocabulary::std_vocabulary(t)).expect("loads");
assert_eq!(loaded.doc(), Some("why this graph: the spread detects trend turns"));
assert_eq!(doced_json, blueprint_to_json(&loaded).expect("re-serializes"), "round-trip byte-stable");
assert_eq!(
@@ -395,7 +396,7 @@ mod tests {
let tapped_json = blueprint_to_json(&tapped).expect("serializes");
assert_ne!(plain_json, tapped_json, "taps are canonical-byte-bearing");
assert!(!plain_json.contains("\"taps\""), "absent taps emit no key: {plain_json}");
let loaded = blueprint_from_json(&tapped_json, &|t| aura_std::std_vocabulary(t)).expect("loads");
let loaded = blueprint_from_json(&tapped_json, &|t| aura_vocabulary::std_vocabulary(t)).expect("loads");
assert_eq!(loaded.taps(), tapped.taps());
assert_eq!(tapped_json, blueprint_to_json(&loaded).expect("re-serializes"), "round-trip byte-stable");
}
@@ -431,14 +432,14 @@ mod tests {
let json = r#"{"format_version":1,"blueprint":{"name":"x","nodes":[{"primitive":{"type":"NoSuchNode"}}]}}"#;
// `.err().unwrap()` (not `.unwrap_err()`): the Ok type `Composite` holds a
// build closure and is not `Debug`, which `unwrap_err`'s bound would require.
let err = blueprint_from_json(json, &|t| aura_std::std_vocabulary(t)).err().unwrap();
let err = blueprint_from_json(json, &|t| aura_vocabulary::std_vocabulary(t)).err().unwrap();
assert!(matches!(err, LoadError::UnknownNodeType(t) if t == "NoSuchNode"));
}
#[test]
fn unsupported_version_fails_named() {
let json = r#"{"format_version":2,"blueprint":{"name":"x","nodes":[]}}"#;
let err = blueprint_from_json(json, &|t| aura_std::std_vocabulary(t)).err().unwrap();
let err = blueprint_from_json(json, &|t| aura_vocabulary::std_vocabulary(t)).err().unwrap();
assert!(matches!(err, LoadError::UnsupportedVersion { found: 2, supported: 1 }));
}
@@ -466,7 +467,7 @@ mod tests {
assert_ne!(with_unknown, canonical, "probe must actually inject unknown keys");
// tolerated, not refused
let loaded = blueprint_from_json(&with_unknown, &|t| aura_std::std_vocabulary(t))
let loaded = blueprint_from_json(&with_unknown, &|t| aura_vocabulary::std_vocabulary(t))
.expect("an unknown optional field is tolerated, not refused");
// the unknown keys did not leak into the graph: re-serialization equals the
@@ -474,7 +475,7 @@ mod tests {
assert_eq!(blueprint_to_json(&loaded).expect("re-serializes"), canonical);
// and the loaded graph runs bit-identically to the plain canonical one (C1).
let plain = blueprint_from_json(&canonical, &|t| aura_std::std_vocabulary(t)).expect("loads");
let plain = blueprint_from_json(&canonical, &|t| aura_vocabulary::std_vocabulary(t)).expect("loads");
assert_eq!(
run_recording(loaded),
run_recording(plain),
@@ -655,9 +656,9 @@ mod tests {
}
// The resolver used by the gang-serde tests below (mirrors the inline
// `&|t| aura_std::std_vocabulary(t)` closure the rest of this module uses).
// `&|t| aura_vocabulary::std_vocabulary(t)` closure the rest of this module uses).
fn fixture_resolver() -> impl Fn(&str) -> Option<PrimitiveBuilder> {
|t: &str| aura_std::std_vocabulary(t)
|t: &str| aura_vocabulary::std_vocabulary(t)
}
// Shared topology: two open SMAs feeding a `Sub`. `ganged_fixture_named`
+3 -1
View File
@@ -344,7 +344,9 @@ mod tests {
use crate::test_fixtures::sma_cross as hand_sma_cross;
use crate::{Composite, OutField, Role, Target};
use aura_core::{Firing, ScalarKind};
use aura_std::{Bias, Recorder, SimBroker, Sma, Sub};
use aura_backtest::SimBroker;
use aura_std::{Recorder, Sma, Sub};
use aura_strategy::Bias;
use std::sync::mpsc;
/// The `sma_cross` sub-composite authored through the builder.
+8 -7
View File
@@ -472,7 +472,7 @@ pub fn replay(
mod tests {
use super::{GraphSession, Op, OpError};
use aura_core::{BindOpError, Scalar, ScalarKind};
use aura_std::std_vocabulary;
use aura_vocabulary::std_vocabulary;
fn session(name: &str) -> GraphSession<'static> {
GraphSession::new(name, &std_vocabulary)
@@ -736,7 +736,7 @@ mod tests {
Op::Tap { from: "fast.value".into(), as_name: "fast_ma".into() },
Op::Expose { from: "sub.value".into(), as_name: "bias".into() },
];
let flat = super::replay("g", ops, &aura_std::std_vocabulary)
let flat = super::replay("g", ops, &aura_vocabulary::std_vocabulary)
.expect("replays")
.compile_with_params(&[Scalar::i64(2), Scalar::i64(4)])
.expect("compiles");
@@ -949,7 +949,7 @@ mod tests {
let finalize = ops.len();
// `.err()` not `.unwrap_err()`: the Ok arm `Composite` holds build closures
// and is not `Debug`.
let (idx, _err) = super::replay("g", ops, &aura_std::std_vocabulary)
let (idx, _err) = super::replay("g", ops, &aura_vocabulary::std_vocabulary)
.err()
.expect("a cyclic op-list must be rejected (DAG invariant 5 / C9)");
// An eager realisation attributes the fault to the closing connect op; a
@@ -976,7 +976,7 @@ mod tests {
Op::Expose { from: "s.value".into(), as_name: "out".into() },
];
assert!(
super::replay("g", ops, &aura_std::std_vocabulary).is_err(),
super::replay("g", ops, &aura_vocabulary::std_vocabulary).is_err(),
"a self-loop op-list must be rejected (DAG invariant 5 / C9)"
);
}
@@ -990,7 +990,7 @@ mod tests {
];
// `.err().unwrap()` rather than `.unwrap_err()`: the Ok arm `Composite` is
// not `Debug` (it holds build closures), which `unwrap_err` would require.
let err = super::replay("g", ops, &aura_std::std_vocabulary).err().unwrap();
let err = super::replay("g", ops, &aura_vocabulary::std_vocabulary).err().unwrap();
assert_eq!(err, (1, OpError::UnknownNodeType("Nope".into())));
}
@@ -1004,7 +1004,8 @@ mod tests {
fn replay_built_signal_compiles_identically_to_graphbuilder() {
use crate::GraphBuilder;
use aura_core::Scalar;
use aura_std::{Bias, Sma, Sub};
use aura_std::{Sma, Sub};
use aura_strategy::Bias;
// param space of the flat root: fast.length(i64), slow.length(i64),
// bias.scale(f64) — same vector applied to both sides, so it cancels.
@@ -1023,7 +1024,7 @@ mod tests {
Op::Connect { from: "sub.value".into(), to: "bias.signal".into() },
Op::Expose { from: "bias.bias".into(), as_name: "bias".into() },
];
let replay_flat = super::replay("root", ops, &aura_std::std_vocabulary)
let replay_flat = super::replay("root", ops, &aura_vocabulary::std_vocabulary)
.expect("replay resolves")
.compile_with_params(&params)
.expect("replay compiles");
+2 -1
View File
@@ -321,7 +321,8 @@ mod tests {
use aura_core::{
Cell, FieldSpec, Node, NodeSchema, PortSpec, PrimitiveBuilder, Scalar,
};
use aura_std::{Bias, Recorder, Sma, Sub};
use aura_std::{Recorder, Sma, Sub};
use aura_strategy::Bias;
use std::sync::mpsc;
/// A bare node whose only purpose is to back a `PrimitiveBuilder` in a test.
+3 -1
View File
@@ -676,7 +676,9 @@ mod tests {
// PortSpec / NodeSchema name the fixtures' declared signatures, brought in here
// (production code reads them via the carried signatures, never naming the types).
use aura_core::{FieldSpec, NodeSchema, PortSpec};
use aura_std::{Bias, Recorder, Sma, SimBroker, Sub, When};
use aura_backtest::SimBroker;
use aura_std::{Recorder, Sma, Sub, When};
use aura_strategy::Bias;
use std::sync::mpsc;
/// Build an f64 source stream from (timestamp, value) points.
+3 -1
View File
@@ -312,7 +312,9 @@ mod tests {
use super::*;
use crate::{Edge, FlatGraph, Harness, SourceSpec, Target, VecSource};
use aura_core::{Firing, NodeSchema, PortSpec, ScalarKind};
use aura_std::{Bias, Recorder, SimBroker, Sma, Sub};
use aura_backtest::SimBroker;
use aura_std::{Recorder, Sma, Sub};
use aura_strategy::Bias;
use std::sync::mpsc;
#[test]
+3 -1
View File
@@ -6,7 +6,9 @@
use crate::{BlueprintNode, Composite, Edge, OutField, Role, Target};
use aura_core::{Firing, Scalar, ScalarKind, Timestamp};
use aura_std::{Bias, Recorder, SimBroker, Sma, Sub};
use aura_backtest::SimBroker;
use aura_std::{Recorder, Sma, Sub};
use aura_strategy::Bias;
use std::sync::mpsc;
/// Seven synthetic F64 ticks driving the harness; deterministic input fixture.