C28 phase 2 (Stratification); realizes item 1 of the deferred #147. The
engine's production surface no longer names a backtest-metric type:
- RunReport becomes generic over its metric payload M; sweep/mc/walkforward/
blueprint thread the parameter (SweepPoint<M>, SweepFamily<M>, WindowRun<M>,
WalkForwardResult<M>). RunManifest stays concrete and engine-owned (its
selection: Option<FamilySelection> embeds the foundation-grade analysis type).
- summarize and the MC assembly (McDraw/McFamily/McAggregate/RBootstrap/
r_bootstrap/monte_carlo) move to aura-backtest - McAggregate::from_draws reads
RunMetrics fields by name, so generifying it is the phase-6 metric-vocabulary
abstraction (#147 item 2), still deferred; wholesale relocation is the honest
cut. The concrete instantiation lives in aura-backtest as
`type RunReport = aura_engine::RunReport<RunMetrics>` + sibling aliases.
- the statistics kernel (MetricStats/quantile/resample_block/SplitMix64) moves
to the aura-analysis foundation; the engine re-imports it (inner->foundation,
legal) and re-exports it so existing consumers stay source-compatible.
Dependency inversion in one commit: aura-engine drops aura-backtest from
[dependencies] (back to dev-deps for its SimBroker/RunMetrics test fixtures);
aura-backtest gains aura-engine. Cycle-free for lib targets - the cycle closes
only through the engine's dev-dep edge, the pattern aura-vocabulary already
uses. aura-backtest reaches the kernel transitively through the engine
re-export, so no aura-backtest -> aura-analysis edge exists (the C28 ladder
permits backtest -> {core, engine} only). run_indexed / SplitMix64::next_f64
widened pub(crate) -> pub for cross-crate use.
Consumers (registry/campaign/cli/composites/ingest/bench) rewired by import
path only, no call-site logic changed. The c28_layering structural test extends
to the full ladder: aura-analysis (no aura-* deps), aura-engine ⊆ {core,
analysis}, aura-backtest ⊆ {core, engine}.
Behaviour-preserving: 1448/0 tests, clippy -D warnings clean, serde shapes
byte-identical (C18 - RunReport<M> keeps field order manifest,metrics; the
CLI pre-serialized-splice contract unchanged), moved code traceable via git
rename detection. Cycle-introduced broken intra-doc links fixed.
closes#292
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
RunManifest gains defaults: Vec<(String, Scalar)> — the wrap-prefixed
bound_param_space() of the signal, read after axis reopening, so a
bound param an axis overrode has already left the space and flows
through params instead (disjoint by construction; verified end to end:
a sweep member's overridden fast.length sits in params while
slow.length/bias.scale sit in defaults). params keeps its "what varied"
semantics and stays the reproduce input. One-directional serde
widening (#[serde(default)]) per the selection/instrument/topology_hash
idiom — old records deserialize with an empty defaults; unlike the
Option fields it always serializes, mirroring params. ~20
struct-literal sites across five crates gained the field
(compile-mandated breadth, no behaviour change at those sites).
The C14 ledger records the underlying decision (2026-07-13): generated
outcome records spend redundancy on direct readability (single writer,
cannot drift); authored intent artifacts admit none (every redundancy
is a drift site) — so the fix lands in the manifest, never the
blueprint.
Verification: RED test run_manifest_stamps_untouched_bound_defaults
green; cargo build --workspace; cargo test --workspace green; clippy
-D warnings on the touched crates; binary-level sweep exclusivity
check.
aura-engine: ListSpace — explicit point set implementing Space beside
GridSpace/RandomSpace (a gate's survivor subset has no cartesian
structure); GridSpace-identical arity/kind validation reusing the
existing SweepError variants (KindMismatch.value_index carries the
point ordinal for a list); empty point list is valid and sweeps to an
empty family; re-exported at the crate root.
aura-registry: CampaignRunRecord/CellRealization/StageRealization/
StageSelection — the thin campaign-level linking record over untouched
family records (#198 decision 4) — with append_campaign_run (assigns
run via next_campaign_run, a parallel counter beside next_run) and
load_campaign_runs (missing file => empty) on the campaign_runs.jsonl
sibling store; blueprint_path and find_blueprint_by_identity promoted
to pub (the campaign executor resolves the same refs the referential
tier validates).
Gates: engine 290/0, registry 54/0, clippy -D warnings clean.
refs #198