diff --git a/Cargo.lock b/Cargo.lock index ff4f20c..ac066d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,6 +50,7 @@ dependencies = [ name = "aura-cli" version = "0.1.0" dependencies = [ + "aura-composites", "aura-core", "aura-engine", "aura-ingest", @@ -60,6 +61,16 @@ dependencies = [ "serde_json", ] +[[package]] +name = "aura-composites" +version = "0.1.0" +dependencies = [ + "aura-core", + "aura-engine", + "aura-std", + "serde_json", +] + [[package]] name = "aura-core" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 3d0db60..de2b06d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "crates/aura-core", "crates/aura-std", "crates/aura-engine", + "crates/aura-composites", "crates/aura-cli", "crates/aura-ingest", "crates/aura-registry", diff --git a/crates/aura-cli/Cargo.toml b/crates/aura-cli/Cargo.toml index 2a0fd6e..27f8627 100644 --- a/crates/aura-cli/Cargo.toml +++ b/crates/aura-cli/Cargo.toml @@ -12,6 +12,9 @@ path = "src/main.rs" [dependencies] aura-core = { path = "../aura-core" } aura-engine = { path = "../aura-engine" } +# aura-composites: the Stage-1 RiskExecutor / vol_stop composite-builders the +# stage1-r harness wires (kept out of aura-engine so the engine stays domain-free). +aura-composites = { path = "../aura-composites" } aura-registry = { path = "../aura-registry" } aura-std = { path = "../aura-std" } aura-ingest = { path = "../aura-ingest" } diff --git a/crates/aura-cli/src/main.rs b/crates/aura-cli/src/main.rs index 6ce7bd6..1a74249 100644 --- a/crates/aura-cli/src/main.rs +++ b/crates/aura-cli/src/main.rs @@ -15,12 +15,12 @@ mod render; use render::{ChartData, ChartMeta, ChartMode, ReduceKind, Series}; use aura_core::{zip_params, Cell, Firing, Scalar, ScalarKind, Timestamp}; +use aura_composites::{risk_executor, StopRule}; use aura_engine::{ - f64_field, join_on_ts, monte_carlo, param_stability, risk_executor, summarize, summarize_r, - walk_forward, window_of, ColumnarTrace, Composite, Edge, FlatGraph, GraphBuilder, Harness, - JoinedRow, McAggregate, McFamily, RollMode, RunManifest, RunReport, SourceSpec, StopRule, - SweepFamily, SyntheticSpec, Target, VecSource, WalkForwardResult, WindowBounds, WindowRoller, - WindowRun, + f64_field, join_on_ts, monte_carlo, param_stability, summarize, summarize_r, walk_forward, + window_of, ColumnarTrace, Composite, Edge, FlatGraph, GraphBuilder, Harness, JoinedRow, + McAggregate, McFamily, RollMode, RunManifest, RunReport, SourceSpec, SweepFamily, + SyntheticSpec, Target, VecSource, WalkForwardResult, WindowBounds, WindowRoller, WindowRun, }; use aura_registry::{ group_families, mc_member_reports, optimize, rank_by, sweep_member_reports, diff --git a/crates/aura-composites/Cargo.toml b/crates/aura-composites/Cargo.toml new file mode 100644 index 0000000..4b9d90b --- /dev/null +++ b/crates/aura-composites/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "aura-composites" +edition.workspace = true +version.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +# aura-composites is the composition layer: `GraphBuilder`/`Composite` come from the +# engine, the wired nodes from aura-std. It lives in its own crate so the engine +# stays domain-free (-> aura-core only) — see src/lib.rs for the full rationale. +# This is the single layer that couples both, and the graph stays acyclic +# (aura-composites -> {aura-engine, aura-std} -> aura-core). +aura-core = { path = "../aura-core" } +aura-engine = { path = "../aura-engine" } +aura-std = { path = "../aura-std" } + +[dev-dependencies] +# serde_json: the risk_executor fixture round-trips a folded RunMetrics through +# serde to prove the `r` block survives (RunMetrics lives in aura-engine). +serde_json = { workspace = true } diff --git a/crates/aura-engine/src/composites.rs b/crates/aura-composites/src/lib.rs similarity index 71% rename from crates/aura-engine/src/composites.rs rename to crates/aura-composites/src/lib.rs index 3313255..dd10bbe 100644 --- a/crates/aura-engine/src/composites.rs +++ b/crates/aura-composites/src/lib.rs @@ -1,18 +1,26 @@ -//! Reusable Stage-1 composite-builders: the volatility stop and the per-symbol -//! RiskExecutor, promoted from the iter-2 integration-test fixtures so the CLI -//! harness (and any consumer) can wire them. Both are `GraphBuilder` compositions -//! of `aura-std` primitives — the engine's convenience layer over the standard -//! nodes (the sibling of `report::summarize_r`, which likewise lives in this crate -//! and reads the `aura-std` PositionManagement record). The Veto is a documented -//! seam, not a node (a pass-through identity is exactly what C19/C23 DCE deletes), -//! so it appears nowhere here. +//! `aura-composites` — reusable Stage-1 composite-builders: the volatility stop and +//! the per-symbol RiskExecutor. Both are `GraphBuilder` compositions (from +//! `aura-engine`) of `aura-std` primitives, so this crate sits *above* both and is +//! the single place where the engine's builder and the standard nodes are wired +//! together. +//! +//! It lives in its own crate on purpose. The engine routes type-erased `Scalar` +//! records and never names a concrete node — `aura_engine::summarize_r` reads the +//! `PositionManagement` record positionally, by column index, without linking +//! `aura-std`. Keeping these node-naming builders out of `aura-engine` preserves +//! that: the engine stays `-> aura-core` only (domain-free), `aura-std` stays the +//! lean node library (`-> aura-core`), and the `aura-engine -> aura-std` edge these +//! builders would otherwise force is dissolved. `aura-composites -> {aura-engine, +//! aura-std}` is the one layer that couples both, and the graph stays acyclic. +//! +//! The Veto is a documented seam, not a node (a pass-through identity is exactly +//! what C19/C23 DCE deletes), so it appears nowhere here. use aura_core::Scalar; +use aura_engine::{Composite, GraphBuilder}; use aura_std::{ Delay, Ema, FixedStop, LinComb, Mul, PositionManagement, Sizer, Sqrt, Sub, PM_FIELD_NAMES, }; -use crate::{Composite, GraphBuilder}; - /// The volatility stop as a composition of primitives: /// `stop_distance = k · Sqrt(Ema(Mul(Δ,Δ), length))`, `Δ = price − Delay(price, 1)` /// — a rolling EWMA standard deviation. Role: input `price` → output `stop_distance`. @@ -40,7 +48,7 @@ pub fn vol_stop(length: i64, k: f64) -> Composite { /// volatility-scaled `vol_stop`. Both expose `price → stop_distance`, so the /// RiskExecutor embeds either by identical downstream wiring. /// -/// Mirrors the sibling axis enum [`RollMode`](crate::RollMode); `Eq` is omitted +/// Mirrors the sibling axis enum [`RollMode`](aura_engine::RollMode); `Eq` is omitted /// because the `f64` payloads forbid it. #[derive(Clone, Copy, Debug, PartialEq)] pub enum StopRule { diff --git a/crates/aura-engine/tests/risk_executor.rs b/crates/aura-composites/tests/risk_executor.rs similarity index 98% rename from crates/aura-engine/tests/risk_executor.rs rename to crates/aura-composites/tests/risk_executor.rs index 920620a..d83bd89 100644 --- a/crates/aura-engine/tests/risk_executor.rs +++ b/crates/aura-composites/tests/risk_executor.rs @@ -8,7 +8,8 @@ use aura_core::{ Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, PortSpec, PrimitiveBuilder, Scalar, ScalarKind, Timestamp, }; -use aura_engine::{risk_executor, summarize_r, GraphBuilder, RunMetrics, StopRule, VecSource}; +use aura_composites::{risk_executor, StopRule}; +use aura_engine::{summarize_r, GraphBuilder, RunMetrics, VecSource}; use aura_std::{Recorder, PM_FIELD_NAMES, PM_RECORD_KINDS}; use std::sync::mpsc::channel; diff --git a/crates/aura-engine/tests/vol_stop_composite.rs b/crates/aura-composites/tests/vol_stop_composite.rs similarity index 96% rename from crates/aura-engine/tests/vol_stop_composite.rs rename to crates/aura-composites/tests/vol_stop_composite.rs index bc6837d..1a831f6 100644 --- a/crates/aura-engine/tests/vol_stop_composite.rs +++ b/crates/aura-composites/tests/vol_stop_composite.rs @@ -4,7 +4,8 @@ //! `GraphBuilder`, bootstraps, and computes the right number end-to-end — the principle //! that a node expressible as a DAG of primitives is a composition, not a fused node. use aura_core::{Firing, Scalar, ScalarKind, Timestamp}; -use aura_engine::{vol_stop, GraphBuilder, VecSource}; +use aura_composites::vol_stop; +use aura_engine::{GraphBuilder, VecSource}; use aura_std::Recorder; use std::sync::mpsc::channel; diff --git a/crates/aura-engine/Cargo.toml b/crates/aura-engine/Cargo.toml index da0bb63..582eea2 100644 --- a/crates/aura-engine/Cargo.toml +++ b/crates/aura-engine/Cargo.toml @@ -14,13 +14,15 @@ serde = { workspace = true } # serde_json renders RunReport JSON for both the registry (disk) and stdout # (RunReport::to_json) — one shape, no hand-rolled writer (amended C16). serde_json = { workspace = true } -# aura-std's standard nodes back the public composite-builders in src/composites.rs -# (vol_stop, risk_executor) — the engine's convenience layer over the standard nodes. -# aura-std depends only on aura-core, so this stays acyclic (aura-engine -> aura-std -# -> aura-core). -aura-std = { path = "../aura-std" } [dev-dependencies] +# aura-std is a TEST-ONLY dependency: the engine's own integration tests +# (stage1_r_e2e, random_sweep_e2e, ger40_breakout) drive real standard nodes through +# a bootstrapped Harness. The engine's library code never names a concrete node — it +# routes type-erased Scalar records (summarize_r reads the PositionManagement record +# positionally, by index), so aura-std stays out of [dependencies] and the engine is +# `-> aura-core` only. The node-naming composite-builders live in `aura-composites`. +aura-std = { path = "../aura-std" } # chrono / chrono-tz build the Berlin-local-wall-clock epoch-ns timestamps the # GER40 session-breakout E2E fixture (tests/ger40_breakout.rs) feeds the engine, # exactly as `Session`'s own unit test does. Pinned to aura-std's versions (the diff --git a/crates/aura-engine/src/lib.rs b/crates/aura-engine/src/lib.rs index b1adb9f..77f45bb 100644 --- a/crates/aura-engine/src/lib.rs +++ b/crates/aura-engine/src/lib.rs @@ -44,7 +44,6 @@ mod blueprint; mod builder; -mod composites; mod graph_model; mod harness; mod mc; @@ -57,7 +56,6 @@ pub use blueprint::{ Role, SweepBinder, }; pub use builder::{BuildError, GraphBuilder, InPort, NodeHandle, OutPort, RoleHandle}; -pub use composites::{risk_executor, vol_stop, StopRule}; pub use graph_model::model_to_json; pub use harness::{ window_of, BootstrapError, Edge, FlatGraph, Harness, Source, SourceSpec, SyntheticSpec, Target,