refactor(composites): extract Stage-1 composite-builders into their own crate
Dissolve the aura-engine -> aura-std dependency by moving the vol_stop /
risk_executor composite-builders out of aura-engine into a new aura-composites
crate. This restores the engine's domain-free layering, which the
composites-in-engine placement (cycle 0065) had inverted.
Why: the engine routes type-erased Scalar records and never names a concrete
node -- summarize_r reads the PositionManagement record positionally, by column
index, without linking aura-std (which is exactly why aura-std was a dev-only
dependency before 0065). Parking the node-naming composite-builders inside
aura-engine forced the engine's production code to reference the node library
for the first time (aura-engine -> aura-std), an upside-down layering: the
engine is the foundation the node library builds upon, not the reverse.
The clean home is a dedicated crate that depends on BOTH the engine's
GraphBuilder and the std nodes, leaving each lower crate untouched:
aura-composites -> { aura-engine, aura-std } -> aura-core (acyclic)
aura-engine -> aura-core only (domain-free again)
aura-std -> aura-core only (lean node library)
aura-std reverts to a [dev-dependency] of aura-engine (the engine's own E2E
tests -- stage1_r_e2e, random_sweep_e2e, ger40_breakout -- still drive real
std nodes through a bootstrapped Harness; that test-only edge creates no cycle).
Moved with history (git mv):
- crates/aura-engine/src/composites.rs -> crates/aura-composites/src/lib.rs
- crates/aura-engine/tests/risk_executor.rs -> crates/aura-composites/tests/
- crates/aura-engine/tests/vol_stop_composite.rs -> crates/aura-composites/tests/
Consumers rewired: aura-cli imports risk_executor / StopRule from aura_composites;
the moved tests import the builders from the crate under test. The intra-doc link
to RollMode and the module rationale doc are updated for the new home.
Behaviour-preserving: workspace build clean; clippy --all-targets -D warnings
clean; full suite 513 passed / 0 failed, identical to baseline (the two moved
test files run unchanged under aura-composites); cargo doc --no-deps clean (no
broken intra-doc links).
This commit is contained in:
Generated
+11
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 }
|
||||
@@ -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 {
|
||||
+2
-1
@@ -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;
|
||||
|
||||
+2
-1
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user