rename: retire the stage1-* family for the r-family (r-sma / r-breakout / r-meanrev)

The stage1 ordinal has been dead vocabulary since the C10 reframe dropped
the two-stage research model: a 1 structurally implies a 2 that no longer
exists. The family is renamed by its live discriminator - the R yardstick -
with members named by their signal, uniform with their signal-named
siblings (sma, macd, momentum):

- selectors: stage1-r -> r-sma, stage1-breakout -> r-breakout,
  stage1-meanrev -> r-meanrev (old tokens are usage errors, exit 2 - no
  silent alias)
- identifiers: Strategy::RSma/RBreakout/RMeanRev, HarnessKind::RSma,
  r_sma_*/r_breakout_*/r_meanrev_*, wrap_r, run_signal_r, RGrid, R_SMA_*
- persisted identity: the sma_signal composite (param prefix
  sma_signal.fast.length / .slow.length; fixtures regenerated; content-ids
  shift - no test pins a literal hash, the registry parses no record names)
- e2e test files git-mv'd to r_sma_e2e / r_breakout_e2e / r_meanrev_e2e
- dead Stage-1/Stage-2 prose reworded to post-C10 vocabulary across
  rustdoc, Cargo.tomls, ledger live lines, and the glossary (historical
  entries stay; fieldtests corpus untouched)
- CLAUDE.md invariant 7 rewritten to record the ratified C10 reframe
  faithfully (the token-swap alone would have laundered the retired
  gated-currency/realistic-broker design into unmarked live prose); the
  unbacked account-mode clause dropped

Verification: cargo build/test --workspace green (51 targets), clippy
-D warnings clean, doc build clean, acceptance grep gate leaves exactly
the one resampling-stage false positive (harness.rs), smoke: --harness
r-sma runs, --harness stage1-r exits 2 with the new usage line.

Decision log: forks and rationale recorded on the issue (reconciliation
+ implementation-phase comments).

closes #174
This commit is contained in:
2026-07-02 12:03:09 +02:00
parent e1b04439c0
commit 4de6d5cbad
22 changed files with 582 additions and 583 deletions
+24 -25
View File
@@ -84,31 +84,30 @@ design decision, not a refactor, and belongs in the ledger.
makes a live external call mid-replay. (See `~/.claude/CLAUDE.md` for the makes a live external call mid-replay. (See `~/.claude/CLAUDE.md` for the
IONOS consent rule: external LLM calls happen at the recording/live-source IONOS consent rule: external LLM calls happen at the recording/live-source
edge, with explicit per-session consent, never inside a backtest.) edge, with explicit per-session consent, never inside a backtest.)
7. **Strategy output is a directional bias stream; risk-based execution and 7. **Strategy output is a directional bias stream; risk-based execution is a
realistic brokers are decoupled downstream layers; signal quality is measured decoupled downstream layer; signal quality is measured in R.** The DAG
in R.** The DAG expresses one state at t (C8: ≤1 record per `eval`), so a expresses one state at t (C8: ≤1 record per `eval`), so a strategy's primary,
strategy's primary, backtestable output is a signed, bounded **bias** `f64 ∈ backtestable output is a signed, bounded **bias** `f64 ∈ [-1,+1]` per cycle —
[-1,+1]` per cycle — direction (sign) + conviction (magnitude, optional), direction (sign) + conviction (magnitude, optional), **unsized** (not an
**unsized** (not an equity curve, not a position, not a size). Sizing leaves the equity curve, not a position, not a size). Sizing leaves the strategy: a
strategy: a decoupled **risk-based execution** layer `bias → stop-rule → Sizer → decoupled **risk-based execution** layer `bias → stop-rule →
Veto → position-management` (the **RiskExecutor** composite, per symbol, nested position-management` (the **RiskExecutor** composite, per symbol; the **Veto**
in a Broker/Account composite) turns bias + a protective **stop** into a sized an optional documented pre-trade-gate seam) turns bias + a protective **stop**
intent. The **stop defines the risk unit R** (1R = the loss if stopped). into a managed position, in R. The **stop defines the risk unit R** (1R = the
**Signal quality is measured in R** (R-multiples / expectancy), the account- and loss if stopped). **Signal quality is measured in R** (R-multiples /
instrument-agnostic yardstick — **Stage 1**: flat-1R sizing, feed-forward, no expectancy), the account- and instrument-agnostic yardstick — the research
equity feedback, the primary research loop. **Currency P&L is Stage 2** (deploy loop is **pure feed-forward**: flat-1R, gross R → net R via the composable
viability): fixed-fractional sizing reads equity (compounding) → realistic cost-model graph (C10), no Sizer, no equity feedback, no `z⁻¹` register.
brokers apply real frictions → currency equity, entered only after `E[R] > 0`. **Money is a live/deploy-edge concern**: currency P&L, fixed-fractional
The single feedback (equity → Sizer) is cut by a `z⁻¹` register on the **fill sizing, and compounding are post-hoc money-management transforms of the net-R
edge** (mark-to-market stays a same-cycle price read), encapsulated in the sequence at the deploy/account layer, and the live broker is an **I/O
executor composite; flat-1R vs compounding is a **structural axis** (C11). The adapter** at the recording/deploy edge (C11/C13), never part of the strategy.
broker-independent **position-event table** (`event_ts, action[buy/sell/close], The broker-independent **position-event table** (`event_ts,
position_id, instrument_id, volume`) remains the **decoupled, derived** Stage-2 action[buy/sell/close], position_id, instrument_id, volume`) survives as the
audit layer — the first difference of the *book* (`deal = target book **decoupled, derived** deploy/reconciliation audit artifact — the first
in_flight`), a computed table (not a per-`eval` output, since one decision difference of the *book* (`deal = target book in_flight`), a computed
instant may yield >1 event) — feeding realistic brokers. Brokers/executors are table (not a per-`eval` output, since one decision instant may yield >1
ordinary downstream nodes, never part of the strategy; account mode event).
(netting/hedging) is a composition constraint.
8. **Deploy artifacts are frozen.** Hot-reload (cdylib) is an authoring-loop 8. **Deploy artifacts are frozen.** Hot-reload (cdylib) is an authoring-loop
tool only. The live bot is a statically-linked, versioned, frozen artifact — tool only. The live bot is a statically-linked, versioned, frozen artifact —
never hot-swapped (audit trail: this bot = this commit). never hot-swapped (audit trail: this bot = this commit).
+6 -6
View File
@@ -71,7 +71,7 @@ pub struct RunMetrics {
/// `runs.jsonl` lines still deserialise (C14/C18 back-compat). /// `runs.jsonl` lines still deserialise (C14/C18 back-compat).
#[serde(alias = "exposure_sign_flips")] #[serde(alias = "exposure_sign_flips")]
pub bias_sign_flips: u64, pub bias_sign_flips: u64,
/// Optional Stage-1 R metrics block. `None` for a pip-only run (and for legacy /// Optional R metrics block. `None` for a pip-only run (and for legacy
/// `runs.jsonl` written before this field existed — `serde(default)`); omitted from /// `runs.jsonl` written before this field existed — `serde(default)`); omitted from
/// the JSON entirely when absent (`skip_serializing_if`), so the pip-only on-disk /// the JSON entirely when absent (`skip_serializing_if`), so the pip-only on-disk
/// shape stays byte-unchanged (C14/C18 back-compat). /// shape stays byte-unchanged (C14/C18 back-compat).
@@ -79,7 +79,7 @@ pub struct RunMetrics {
pub r: Option<RMetrics>, pub r: Option<RMetrics>,
} }
/// R-based signal-quality metrics (Stage-1), reduced from a `PositionManagement` dense /// R-based signal-quality metrics, reduced from a `PositionManagement` dense
/// record stream by [`summarize_r`]. Account- and instrument-agnostic (pure R). Carries /// record stream by [`summarize_r`]. Account- and instrument-agnostic (pure R). Carries
/// the enriched dispersion/churn fields (SQN, conviction terciles, net-of-cost). /// the enriched dispersion/churn fields (SQN, conviction terciles, net-of-cost).
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
@@ -129,7 +129,7 @@ impl PartialEq for RMetrics {
// Dense `PositionManagement` record column indices — the lockstep contract with // Dense `PositionManagement` record column indices — the lockstep contract with
// aura-std's FIELD_NAMES/RECORD_KINDS. The record crosses the crate boundary as a // aura-std's FIELD_NAMES/RECORD_KINDS. The record crosses the crate boundary as a
// type-erased `Scalar` vector (C4 SoA), so it is read positionally, not by importing // type-erased `Scalar` vector (C4 SoA), so it is read positionally, not by importing
// the producer's types; the layout is shared by convention and `stage1_r_e2e.rs` // the producer's types; the layout is shared by convention and `r_sma_e2e.rs`
// guards that it matches. // guards that it matches.
mod r_col { mod r_col {
pub const CLOSED: usize = 0; pub const CLOSED: usize = 0;
@@ -324,7 +324,7 @@ pub fn summarize_r(
/// `summarize_r_includes_open_trade_and_matches_r_metrics_from_rs` — touch one copy /// `summarize_r_includes_open_trade_and_matches_r_metrics_from_rs` — touch one copy
/// without the other and that test is the sole tripwire. The /// without the other and that test is the sole tripwire. The
/// fields a flat R series cannot carry are set honestly: `n_open_at_end = 0`, /// fields a flat R series cannot carry are set honestly: `n_open_at_end = 0`,
/// `net_expectancy_r = expectancy_r` (exact under the Stage-1 cost = 0 invariant), /// `net_expectancy_r = expectancy_r` (exact under the cost = 0 invariant),
/// `conviction_terciles_r = [0,0,0]` (per-trade conviction is not pooled). Empty /// `conviction_terciles_r = [0,0,0]` (per-trade conviction is not pooled). Empty
/// input -> a well-defined all-zero `RMetrics`. /// input -> a well-defined all-zero `RMetrics`.
pub fn r_metrics_from_rs(rs: &[f64]) -> RMetrics { pub fn r_metrics_from_rs(rs: &[f64]) -> RMetrics {
@@ -375,7 +375,7 @@ pub fn r_metrics_from_rs(rs: &[f64]) -> RMetrics {
n_open_at_end: 0, n_open_at_end: 0,
sqn, sqn,
sqn_normalized, sqn_normalized,
net_expectancy_r: mean, // cost = 0 -> net == gross (Stage-1 frictionless) net_expectancy_r: mean, // cost = 0 -> net == gross (frictionless)
conviction_terciles_r: [0.0; 3], conviction_terciles_r: [0.0; 3],
trade_rs: Vec::new(), trade_rs: Vec::new(),
} }
@@ -1108,7 +1108,7 @@ mod tests {
#[test] #[test]
fn r_metrics_from_rs_folds_a_flat_series() { fn r_metrics_from_rs_folds_a_flat_series() {
// pooled across-window R series [2.0, -1.0, 1.0]: expectancy = 2/3, 2 wins of 3, // pooled across-window R series [2.0, -1.0, 1.0]: expectancy = 2/3, 2 wins of 3,
// profit_factor = (2+1)/1 = 3. At cost 0 (frictionless Stage-1) net == gross. // profit_factor = (2+1)/1 = 3. At cost 0 (frictionless) net == gross.
// conviction terciles are not pooled -> [0,0,0]; n_open_at_end is not a pooled // conviction terciles are not pooled -> [0,0,0]; n_open_at_end is not a pooled
// concept -> 0. // concept -> 0.
let m = r_metrics_from_rs(&[2.0, -1.0, 1.0]); let m = r_metrics_from_rs(&[2.0, -1.0, 1.0]);
+2 -2
View File
@@ -12,8 +12,8 @@ path = "src/main.rs"
[dependencies] [dependencies]
aura-core = { path = "../aura-core" } aura-core = { path = "../aura-core" }
aura-engine = { path = "../aura-engine" } aura-engine = { path = "../aura-engine" }
# aura-composites: the Stage-1 RiskExecutor / vol_stop composite-builders the # aura-composites: the RiskExecutor / vol_stop composite-builders the
# stage1-r harness wires (kept out of aura-engine so the engine stays domain-free). # r-sma harness wires (kept out of aura-engine so the engine stays domain-free).
aura-composites = { path = "../aura-composites" } aura-composites = { path = "../aura-composites" }
aura-registry = { path = "../aura-registry" } aura-registry = { path = "../aura-registry" }
aura-std = { path = "../aura-std" } aura-std = { path = "../aura-std" }
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
{"format_version":1,"blueprint":{"name":"sma_signal","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":2}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}}
+1
View File
@@ -0,0 +1 @@
{"format_version":1,"blueprint":{"name":"sma_signal","nodes":[{"primitive":{"type":"SMA","name":"fast"}},{"primitive":{"type":"SMA","name":"slow"}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}}
-1
View File
@@ -1 +0,0 @@
{"format_version":1,"blueprint":{"name":"stage1_signal","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":2}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}}
@@ -1 +0,0 @@
{"format_version":1,"blueprint":{"name":"stage1_signal","nodes":[{"primitive":{"type":"SMA","name":"fast"}},{"primitive":{"type":"SMA","name":"slow"}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}}
+1 -1
View File
@@ -1 +1 @@
{"format_version":1,"blueprint":{"name":"stage1_signal","nodes":[{"primitive":{"type":"Nope","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":2}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}} {"format_version":1,"blueprint":{"name":"sma_signal","nodes":[{"primitive":{"type":"Nope","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":2}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}}
+1 -1
View File
@@ -234,7 +234,7 @@ fn graph_build_renders_role_kind_mismatch_at_finalize_by_identifier() {
/// runs and distinct for a structurally different topology. It is the same hash `aura run` /// runs and distinct for a structurally different topology. It is the same hash `aura run`
/// stamps as `topology_hash` (both via the one `content_id` primitive over the canonical /// stamps as `topology_hash` (both via the one `content_id` primitive over the canonical
/// `blueprint_to_json` bytes), so a data-authored topology has a stable, comparable content /// `blueprint_to_json` bytes), so a data-authored topology has a stable, comparable content
/// id. (Note: an op-script and the Rust `stage1_signal` builder produce *different* /// id. (Note: an op-script and the Rust `sma_signal` builder produce *different*
/// canonical forms — composite debug-name, an unbound `Bias.scale` — so they are different /// canonical forms — composite debug-name, an unbound `Bias.scale` — so they are different
/// topologies by the byte definition; content-addressing keys on the canonical form.) /// topologies by the byte definition; content-addressing keys on the canonical form.)
#[test] #[test]
+2 -2
View File
@@ -1,4 +1,4 @@
//! `aura-composites` — reusable Stage-1 composite-builders: the volatility stop and //! `aura-composites` — reusable composite-builders for the feed-forward R loop: the volatility stop and
//! the per-symbol RiskExecutor. Both are `GraphBuilder` compositions (from //! the per-symbol RiskExecutor. Both are `GraphBuilder` compositions (from
//! `aura-engine`) of `aura-std` primitives, so this crate sits *above* both and is //! `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 //! the single place where the engine's builder and the standard nodes are wired
@@ -35,7 +35,7 @@ pub fn vol_stop(length: i64, k: f64) -> Composite {
/// are `named` `stop_length` / `stop_k` so their slots surface in `param_space` under /// are `named` `stop_length` / `stop_k` so their slots surface in `param_space` under
/// the `.stop_length.length` / `.stop_k.weights[0]` suffixes). One body, so the /// the `.stop_length.length` / `.stop_k.weights[0]` suffixes). One body, so the
/// topology — every node, edge, and exposed role — cannot drift between the two arms; /// topology — every node, edge, and exposed role — cannot drift between the two arms;
/// only the two knobs differ (the `Option<i64>`-knob idiom `stage1_r_graph` uses). /// only the two knobs differ (the `Option<i64>`-knob idiom `r_sma_graph` uses).
fn vol_stop_inner(knobs: Option<(i64, f64)>) -> Composite { fn vol_stop_inner(knobs: Option<(i64, f64)>) -> Composite {
let mut g = GraphBuilder::new("vol_stop"); let mut g = GraphBuilder::new("vol_stop");
let price = g.input_role("price"); let price = g.input_role("price");
@@ -1,8 +1,8 @@
//! The `RiskExecutor` composite (Stage-1, #128): a per-symbol risk-based executor over a //! The `RiskExecutor` composite (#128): a per-symbol risk-based executor over a
//! bias stream — `stop-rule -> Sizer -> position-management`, exposing the dense R-record. //! bias stream — `stop-rule -> Sizer -> position-management`, exposing the dense R-record.
//! Proves the composite bootstraps, runs end-to-end, and folds to the SAME R-outcomes as //! Proves the composite bootstraps, runs end-to-end, and folds to the SAME R-outcomes as
//! the hand-wired iter-1 chain, and that R is invariant under the Sizer's `risk_budget` //! the hand-wired iter-1 chain, and that R is invariant under the Sizer's `risk_budget`
//! (Stage-1 feed-forward). The Veto is a DOCUMENTED SEAM, not a runtime node (a //! (feed-forward). The Veto is a DOCUMENTED SEAM, not a runtime node (a
//! pass-through identity is exactly what C19/C23 DCE deletes), so it appears nowhere here. //! pass-through identity is exactly what C19/C23 DCE deletes), so it appears nowhere here.
use aura_core::{ use aura_core::{
Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, PortSpec, PrimitiveBuilder, Scalar, Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, PortSpec, PrimitiveBuilder, Scalar,
@@ -14,7 +14,7 @@ use aura_std::{Recorder, PM_FIELD_NAMES, PM_RECORD_KINDS};
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
// The dense-record columns this fixture reads, named in lockstep with the sibling // The dense-record columns this fixture reads, named in lockstep with the sibling
// `stage1_r_e2e.rs` (which names `REALIZED_R = 1` the same way) so the cross-crate // `r_sma_e2e.rs` (which names `REALIZED_R = 1` the same way) so the cross-crate
// `PM_FIELD_NAMES` layout is never referenced by a bare literal. // `PM_FIELD_NAMES` layout is never referenced by a bare literal.
const REALIZED_R: usize = 1; const REALIZED_R: usize = 1;
const SIZE: usize = 10; const SIZE: usize = 10;
@@ -100,7 +100,7 @@ fn risk_executor_bootstraps_and_folds_to_expected_rmetric() {
assert!((m.expectancy_r - 0.5).abs() < 1e-9, "window-end R = (105-100)/10; got {}", m.expectancy_r); assert!((m.expectancy_r - 0.5).abs() < 1e-9, "window-end R = (105-100)/10; got {}", m.expectancy_r);
} }
/// Property: R is invariant under the Sizer's `risk_budget` (Stage-1 feed-forward). The /// Property: R is invariant under the Sizer's `risk_budget` (feed-forward). The
/// same price path at two budgets yields a bit-identical realised-R ledger, while the /// same price path at two budgets yields a bit-identical realised-R ledger, while the
/// `size` column scales with the budget — proving size flows through the Sizer into PM yet /// `size` column scales with the budget — proving size flows through the Sizer into PM yet
/// never touches R. /// never touches R.
@@ -127,7 +127,7 @@ fn risk_executor_r_invariant_under_risk_budget() {
} }
/// Property: **a real folded `RMetrics` survives the `RunMetrics.r` serde round-trip /// Property: **a real folded `RMetrics` survives the `RunMetrics.r` serde round-trip
/// byte-for-byte (the Stage-1 on-disk back-compat contract), driven from an actual run — /// byte-for-byte (the on-disk back-compat contract), driven from an actual run —
/// not a hand-built literal.** A bootstrapped RiskExecutor run is folded by `summarize_r` /// not a hand-built literal.** A bootstrapped RiskExecutor run is folded by `summarize_r`
/// and the result is attached as `RunMetrics.r = Some(..)`; serializing then /// and the result is attached as `RunMetrics.r = Some(..)`; serializing then
/// deserializing must reproduce an equal value, and the `r` key must be present. The /// deserializing must reproduce an equal value, and the `r` key must be present. The
@@ -148,7 +148,7 @@ fn folded_rmetrics_survives_runmetrics_serde_round_trip() {
} }
/// Property: the RiskExecutor embeds the `vol_stop` composite (the new `StopRule::Vol` /// Property: the RiskExecutor embeds the `vol_stop` composite (the new `StopRule::Vol`
/// arm) and folds to a finite RMetric — the volatility-defined default the `stage1-r` /// arm) and folds to a finite RMetric — the volatility-defined default the `r-sma`
/// harness uses. A short k·σ stop over a rising-then-falling path opens a trade and /// harness uses. A short k·σ stop over a rising-then-falling path opens a trade and
/// (stop or window-end) closes at least one, so the fold is non-empty and sane. /// (stop or window-end) closes at least one, so the fold is non-empty and sane.
#[test] #[test]
@@ -246,7 +246,7 @@ fn risk_executor_vol_open_exposes_the_two_stop_knobs_as_free_axes() {
/// Property (#137): the OPEN vol-stop arm, bootstrapped at a given `(length, k)`, folds to /// Property (#137): the OPEN vol-stop arm, bootstrapped at a given `(length, k)`, folds to
/// the SAME R-outcome as the BOUND arm at the same values — the byte-equivalence the /// the SAME R-outcome as the BOUND arm at the same values — the byte-equivalence the
/// no-flags stage1-r sweep relies on to stay golden (the gridding variant only frees the /// no-flags r-sma sweep relies on to stay golden (the gridding variant only frees the
/// two knobs; topology and every other constant are unchanged). The open arm's positional /// two knobs; topology and every other constant are unchanged). The open arm's positional
/// point is `[length, k]` in `param_space` slot order (length first, then k). /// point is `[length, k]` in `param_space` slot order (length first, then k).
#[test] #[test]
+1 -1
View File
@@ -22,7 +22,7 @@ serde_json = { workspace = true }
[dev-dependencies] [dev-dependencies]
# aura-std is a TEST-ONLY dependency: the engine's own integration tests # 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 # (r_sma_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 # 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 # 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 # positionally, by index), so aura-std stays out of [dependencies] and the engine is
@@ -3,7 +3,7 @@
//! {Gt, Gt} -> {Latch, Latch} -> Sub = bias in {-1,0,+1}. Tests the FADE //! {Gt, Gt} -> {Latch, Latch} -> Sub = bias in {-1,0,+1}. Tests the FADE
//! direction (price above mean+k*sigma -> short -1; below mean-k*sigma -> long //! direction (price above mean+k*sigma -> short -1; below mean-k*sigma -> long
//! +1), the latched hold, and that no fade fires on a flat series. Built //! +1), the latched hold, and that no fade fires on a flat series. Built
//! straight from aura-std nodes (no CLI dependency); mirrors stage1_breakout_e2e. //! straight from aura-std nodes (no CLI dependency); mirrors r_breakout_e2e.
use aura_core::{Scalar, Timestamp}; use aura_core::{Scalar, Timestamp};
use aura_engine::{GraphBuilder, Harness, Source, VecSource}; use aura_engine::{GraphBuilder, Harness, Source, VecSource};
@@ -1,4 +1,4 @@
//! Stage-1 R E2E: a synthetic bias+price chain through stop-rule + position-management, //! R E2E: a synthetic bias+price chain through stop-rule + position-management,
//! recorded and folded by `summarize_r`. Also guards the dense-record layout contract //! recorded and folded by `summarize_r`. Also guards the dense-record layout contract
//! (aura-std's `PM_FIELD_NAMES`/`PM_RECORD_KINDS` vs the column indices `summarize_r` //! (aura-std's `PM_FIELD_NAMES`/`PM_RECORD_KINDS` vs the column indices `summarize_r`
//! reads). //! reads).
@@ -94,7 +94,7 @@ fn synthetic_long_then_stop_produces_a_sane_rmetric() {
/// Drive the real cross-crate chain `stop -> PositionManagement` over a `(bias, price)` /// Drive the real cross-crate chain `stop -> PositionManagement` over a `(bias, price)`
/// path, collecting the producer's dense records (decoded by the producer's own /// path, collecting the producer's dense records (decoded by the producer's own
/// `PM_RECORD_KINDS`, the wire contract) into a ledger — the recorded stream a Stage-1 /// `PM_RECORD_KINDS`, the wire contract) into a ledger — the recorded stream an R-yardstick
/// run would persist. Per-cycle bias (not constant) so a fixture can drop bias to 0 to /// run would persist. Per-cycle bias (not constant) so a fixture can drop bias to 0 to
/// forbid the immediate re-entry after a stop. The fold is left to the caller so the /// forbid the immediate re-entry after a stop. The fold is left to the caller so the
/// same recorded ledger can be summarized at several `round_trip_cost` values. /// same recorded ledger can be summarized at several `round_trip_cost` values.
@@ -124,7 +124,7 @@ fn run_chain_ledger(stop: &mut dyn Node, path: &[(f64, f64)]) -> Vec<(Timestamp,
/// Drive the real cross-crate chain `stop -> PositionManagement -> summarize_r` /// Drive the real cross-crate chain `stop -> PositionManagement -> summarize_r`
/// over a `(bias, price)` path. This is the actual producer->consumer seam, /// over a `(bias, price)` path. This is the actual producer->consumer seam,
/// node-by-node; the resulting `RMetrics` is what a recorded Stage-1 run would report. /// node-by-node; the resulting `RMetrics` is what a recorded R-yardstick run would report.
fn run_chain(stop: &mut dyn Node, path: &[(f64, f64)]) -> RMetrics { fn run_chain(stop: &mut dyn Node, path: &[(f64, f64)]) -> RMetrics {
summarize_r(&run_chain_ledger(stop, path), &[]) summarize_r(&run_chain_ledger(stop, path), &[])
} }
@@ -1,5 +1,5 @@
//! Folded sink reductions are byte-for-byte equal to the raw-recorder path. //! Folded sink reductions are byte-for-byte equal to the raw-recorder path.
//! Drives the folding sinks node-by-node (the `stage1_r_e2e.rs` direct-node //! Drives the folding sinks node-by-node (the `r_sma_e2e.rs` direct-node
//! style) over a synthetic dense R-record + equity/exposure series, and asserts //! style) over a synthetic dense R-record + equity/exposure series, and asserts
//! `summarize_r` over `GatedRecorder`'s emitted rows equals `summarize_r` over //! `summarize_r` over `GatedRecorder`'s emitted rows equals `summarize_r` over
//! the full record, and a `SeriesReducer` summary equals `summarize`'s fields. //! the full record, and a `SeriesReducer` summary equals `summarize`'s fields.
+4 -4
View File
@@ -1,4 +1,4 @@
//! `PositionManagement` — the stateful heart of Stage-1 risk-based execution. Turns a //! `PositionManagement` — the stateful heart of feed-forward risk-based execution. Turns a
//! bias + a protective-stop distance into a stream of realised per-trade R-outcomes. //! bias + a protective-stop distance into a stream of realised per-trade R-outcomes.
//! Emits ONE dense record per cycle (C8, like `SimBroker`; multi-field output on the //! Emits ONE dense record per cycle (C8, like `SimBroker`; multi-field output on the
//! `Resample` precedent): the trade ledger is the rows where `closed_this_cycle`, the //! `Resample` precedent): the trade ledger is the rows where `closed_this_cycle`, the
@@ -146,7 +146,7 @@ impl Node for PositionManagement {
} else { } else {
price.max(p.stop_level) price.max(p.stop_level)
}; };
// size-invariant: R is a pure stop-distance ratio (Stage-1 feed-forward). // size-invariant: R is a pure stop-distance ratio (feed-forward).
realized = p.dir as f64 * (fill - p.entry) / p.latched_dist; realized = p.dir as f64 * (fill - p.entry) / p.latched_dist;
was_stopped = true; was_stopped = true;
reason = ExitReason::Stop as i64; reason = ExitReason::Stop as i64;
@@ -157,7 +157,7 @@ impl Node for PositionManagement {
closed = true; closed = true;
self.pos = None; self.pos = None;
} else if bias_exit { } else if bias_exit {
// size-invariant: R is a pure stop-distance ratio (Stage-1 feed-forward). // size-invariant: R is a pure stop-distance ratio (feed-forward).
realized = p.dir as f64 * (price - p.entry) / p.latched_dist; realized = p.dir as f64 * (price - p.entry) / p.latched_dist;
reason = if sign0(bias) != 0.0 { reason = if sign0(bias) != 0.0 {
ExitReason::ReversalLeg as i64 ExitReason::ReversalLeg as i64
@@ -275,7 +275,7 @@ mod tests {
} }
// (3) R-invariance under size: the Sizer's `size` flows into col 10 but never into R. // (3) R-invariance under size: the Sizer's `size` flows into col 10 but never into R.
// Scaling size leaves every realized_r identical (the property that keeps Stage 1 // Scaling size leaves every realized_r identical (the property that keeps the research loop
// feed-forward); col 10 reflects the size so we know it actually flowed end-to-end. // feed-forward); col 10 reflects the size so we know it actually flowed end-to-end.
#[test] #[test]
fn realized_r_is_invariant_under_size() { fn realized_r_is_invariant_under_size() {
+5 -5
View File
@@ -1,11 +1,11 @@
//! `Sizer` — the flat-1R sizing seam (C10 Stage-1). Turns a protective-stop distance //! `Sizer` — the flat-1R sizing seam (C10). Turns a protective-stop distance
//! into a position `size = risk_budget / stop_distance`: with `risk_budget = 1.0` this is //! into a position `size = risk_budget / stop_distance`: with `risk_budget = 1.0` this is
//! true flat-1R (one risk unit per trade) and `size` is inversely proportional to the //! true flat-1R (one risk unit per trade) and `size` is inversely proportional to the
//! stop — NOT a constant. The `bias` input gates firing (a size is only meaningful when a //! stop — NOT a constant. The `bias` input gates firing (a size is only meaningful when a
//! strategy is taking a position) and is the Stage-2 seam: fixed-fractional sizing swaps //! strategy is taking a position) and is the live/deploy-edge seam: fixed-fractional
//! `risk_budget` for `risk_fraction · equity` (an added equity input), same node shape. //! sizing swaps `risk_budget` for `risk_fraction · equity` (an added equity input), same
//! R is computed SIZE-INVARIANTLY downstream, so the Sizer never contaminates signal //! node shape. R is computed SIZE-INVARIANTLY downstream, so the Sizer never contaminates
//! quality — it only scales the (Stage-2) currency exposure. //! signal quality — it only scales the (deploy-edge) currency exposure.
use aura_core::{ use aura_core::{
Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, ParamSpec, PortSpec, PrimitiveBuilder, Cell, Ctx, FieldSpec, Firing, Node, NodeSchema, ParamSpec, PortSpec, PrimitiveBuilder,
ScalarKind, ScalarKind,
+17 -17
View File
@@ -614,7 +614,7 @@ the ground truth**. The cost model *approximates*; it **never claims realism**.
`SimBroker` (the pip-equity, unsized-exposure node) is the **pre-reframe pip `SimBroker` (the pip-equity, unsized-exposure node) is the **pre-reframe pip
ancestor** — with the net-R cost model it is **redundant as a quality measure** (R ancestor** — with the net-R cost model it is **redundant as a quality measure** (R
supersedes pips). It is retained as a **legacy / simple optional pip yardstick** supersedes pips). It is retained as a **legacy / simple optional pip yardstick**
(still wired in the `stage1-r` harness for an honest dual readout), **not part of (still wired in the `r-sma` harness for an honest dual readout), **not part of
the new model and not to be expanded**. the new model and not to be expanded**.
**Forbids.** Putting **sizing or the stop in the strategy** (bias is unsized; the **Forbids.** Putting **sizing or the stop in the strategy** (bias is unsized; the
@@ -737,7 +737,7 @@ co-temporal cost stream (positional 1:1 join — `cost[i]` is `record[i]`'s cycl
cost = 0 baseline on an empty stream. The headline sink is **`net_r_equity`** = cost = 0 baseline on an empty stream. The headline sink is **`net_r_equity`** =
`LinComb(4)[cum_realized_r, unrealized_r, cum_cost_in_r, open_cost_in_r]` → Recorder `LinComb(4)[cum_realized_r, unrealized_r, cum_cost_in_r, open_cost_in_r]` → Recorder
(C8/C18), a sibling of `r_equity`, emitted only when a cost is authored. Wired on the (C8/C18), a sibling of `r_equity`, emitted only when a cost is authored. Wired on the
**run path** via `--cost-per-trade` (`stage1_r_graph`); sweep / walk-forward / mc pass **run path** via `--cost-per-trade` (`r_sma_graph`); sweep / walk-forward / mc pass
`None` (cost on the reduce-mode sweep path and cost in OOS pooling deferred — the `None` (cost on the reduce-mode sweep path and cost in OOS pooling deferred — the
positional join holds only while one cost node fires in lockstep with PM). Deferred to positional join holds only while one cost node fires in lockstep with PM). Deferred to
later milestone cycles: the general `CostNode` trait + multi-node cost-graph later milestone cycles: the general `CostNode` trait + multi-node cost-graph
@@ -928,8 +928,8 @@ composite-builder (`risk_executor(StopRule, risk_budget)`) with a
`StopRule{Fixed,Vol}` **structural axis** (C20); per the 2026-06-28 reframe its `StopRule{Fixed,Vol}` **structural axis** (C20); per the 2026-06-28 reframe its
Sizer interior and `risk_budget` arg are dropped / vestigial, the **Veto** stays a Sizer interior and `risk_budget` arg are dropped / vestigial, the **Veto** stays a
documented seam, not a runtime node. The layer is operable from the CLI: `aura run documented seam, not a runtime node. The layer is operable from the CLI: `aura run
--harness <sma|macd|stage1-r>` — a compile-time selector over Rust-authored --harness <sma|macd|r-sma>` — a compile-time selector over Rust-authored
harnesses (C9/C17) — folds `summarize_r` into `RunMetrics.r`, the `stage1-r` harnesses (C9/C17) — folds `summarize_r` into `RunMetrics.r`, the `r-sma`
harness fanning one bias into both `SimBroker` (legacy pip) and the RiskExecutor harness fanning one bias into both `SimBroker` (legacy pip) and the RiskExecutor
(R); an `r_equity` tap charts the by-trade R-equity. Composites live in the (R); an `r_equity` tap charts the by-trade R-equity. Composites live in the
dedicated `aura-composites` crate, so `aura-engine`'s runtime dependency stays dedicated `aura-composites` crate, so `aura-engine`'s runtime dependency stays
@@ -937,10 +937,10 @@ dedicated `aura-composites` crate, so `aura-engine`'s runtime dependency stays
stays acyclic). stays acyclic).
**Realization (cycle 0066).** SQN is the operational single-number objective for **Realization (cycle 0066).** SQN is the operational single-number objective for
ranking a Stage-1 sweep family by signal quality — C12 **axis-2 (argmax-metric)** ranking an r-sma sweep family by signal quality — C12 **axis-2 (argmax-metric)**
over the C18 family store. `metric_cmp` (`aura-registry`) learns the over the C18 family store. `metric_cmp` (`aura-registry`) learns the
higher-is-better R metrics `sqn`, `expectancy_r`, `net_expectancy_r`; a member higher-is-better R metrics `sqn`, `expectancy_r`, `net_expectancy_r`; a member
with no `r` block sorts last (`NEG_INFINITY`). `aura sweep --strategy stage1-r` with no `r` block sorts last (`NEG_INFINITY`). `aura sweep --strategy r-sma`
produces the rankable R family, each member folding `summarize_r` into produces the rankable R family, each member folding `summarize_r` into
`RunMetrics.r`. The default grid varies **only the signal** (`fast` / `slow` SMA `RunMetrics.r`. The default grid varies **only the signal** (`fast` / `slow` SMA
lengths), holding the stop and sizing fixed: `risk_budget` is R-invariant and lengths), holding the stop and sizing fixed: `risk_budget` is R-invariant and
@@ -954,8 +954,8 @@ beside the floated knobs (reproducible from its own manifest, C18).
(`SQN_CAP = 100`), turnover-robust where raw `sqn` rewards trade count. It is an (`SQN_CAP = 100`), turnover-robust where raw `sqn` rewards trade count. It is an
**opt-in** rank key (`Metric::SqnNormalized`); raw `sqn` and the default ranker **opt-in** rank key (`Metric::SqnNormalized`); raw `sqn` and the default ranker
stay byte-unchanged, and the field carries `#[serde(default)]` (C18). Below the cap stay byte-unchanged, and the field carries `#[serde(default)]` (C18). Below the cap
(`n ≤ 100`) it equals raw `sqn` exactly. **#135 (stage1-r `--trace`):** (`n ≤ 100`) it equals raw `sqn` exactly. **#135 (r-sma `--trace`):**
`stage1_r_sweep_family` persists each member's equity / exposure / r_equity under `r_sma_sweep_family` persists each member's equity / exposure / r_equity under
`runs/traces/<n>/<member_key>/` via the same `persist_traces_r` the single run uses; `runs/traces/<n>/<member_key>/` via the same `persist_traces_r` the single run uses;
per-member `--trace` is symmetric across all three sweep strategies. per-member `--trace` is symmetric across all three sweep strategies.
@@ -1327,7 +1327,7 @@ one `content_id` primitive `topology_hash` also uses (acc 1); a Tier-1 optional
blueprint does not use leaves the id byte-stable (acc 3, composing #156/#164). blueprint does not use leaves the id byte-stable (acc 3, composing #156/#164).
`serde_json/float_roundtrip` is enabled so stored f64 metrics round-trip exactly through `serde_json/float_roundtrip` is enabled so stored f64 metrics round-trip exactly through
`families.jsonl` — the precondition for a bit-identical compare (C1). **Signal-only this `families.jsonl` — the precondition for a bit-identical compare (C1). **Signal-only this
cycle**: the id covers the signal blueprint; the fixed Stage-1-R scaffolding stays cycle**: the id covers the signal blueprint; the fixed r-sma scaffolding stays
commit-identified (it is not yet blueprint-data, C24). Whole-harness / structural-axis commit-identified (it is not yet blueprint-data, C24). Whole-harness / structural-axis
content-addressing, and whether the id should exclude the composite **debug-name** (a content-addressing, and whether the id should exclude the composite **debug-name** (a
non-load-bearing symbol, invariant 11 — an op-script and the Rust builder produce non-load-bearing symbol, invariant 11 — an op-script and the Rust builder produce
@@ -1887,7 +1887,7 @@ Tier-2 section to validate it; recorded on #156).
**Realization (2026-06-30, cycle 0092 — runs are built FROM blueprint-data, #165).** **Realization (2026-06-30, cycle 0092 — runs are built FROM blueprint-data, #165).**
`aura run <blueprint.json>` now loads a serialized **signal** blueprint `aura run <blueprint.json>` now loads a serialized **signal** blueprint
(`blueprint_from_json` → the closed `std_vocabulary`; an unknown type fails clean as (`blueprint_from_json` → the closed `std_vocabulary`; an unknown type fails clean as
`UnknownNodeType`, the data-plane face of invariant 9), wraps it in the Stage-1-R run `UnknownNodeType`, the data-plane face of invariant 9), wraps it in the r-sma run
scaffolding (sinks / broker / data supplied **at run**, not serialized — C24's deferred scaffolding (sinks / broker / data supplied **at run**, not serialized — C24's deferred
set), runs it, and emits a `RunReport` **bit-identical** (C1) to its Rust-built twin — set), runs it, and emits a `RunReport` **bit-identical** (C1) to its Rust-built twin —
proven by `loaded_signal_runs_bit_identical_to_rust_built`. The `RunManifest` now carries proven by `loaded_signal_runs_bit_identical_to_rust_built`. The `RunManifest` now carries
@@ -1895,8 +1895,8 @@ a **`topology_hash`**: SHA256 of the canonical (#164) `blueprint_to_json`, the #
reproducibility anchor, a Tier-1 optional field (#156). The hash + helper live reproducibility anchor, a Tier-1 optional field (#156). The hash + helper live
research-side (`aura-cli` + `sha2`), off the frozen engine (invariant 8). This is the research-side (`aura-cli` + `sha2`), off the frozen engine (invariant 8). This is the
**keystone of the World/C21 milestone**: topology-as-data is now *runnable*, not only **keystone of the World/C21 milestone**: topology-as-data is now *runnable*, not only
serializable. The harness wrap was made shareable — `stage1_r_graph()` = serializable. The harness wrap was made shareable — `r_sma_graph()` =
`wrap_stage1r(stage1_signal(...))`, so the Rust path and the data path are the same seam `wrap_r(sma_signal(...))`, so the Rust path and the data path are the same seam
over the same signal (a behaviour-preserving C19/C23 restructure). Scope note: the hash over the same signal (a behaviour-preserving C19/C23 restructure). Scope note: the hash
covers the **signal** only (the fixed scaffolding is identified by `commit`); a content-id covers the **signal** only (the fixed scaffolding is identified by `commit`); a content-id
distinguishing harness-structural variants is a #158/#166 concern once scaffolding varies. distinguishing harness-structural variants is a #158/#166 concern once scaffolding varies.
@@ -1904,7 +1904,7 @@ distinguishing harness-structural variants is a #158/#166 concern once scaffoldi
**Realization (2026-07-01, cycle 0093 — the World orchestrates FAMILIES from blueprint-data, **Realization (2026-07-01, cycle 0093 — the World orchestrates FAMILIES from blueprint-data,
#166).** `aura sweep <blueprint.json> --axis <name>=<csv>` loads an **open** signal blueprint, #166).** `aura sweep <blueprint.json> --axis <name>=<csv>` loads an **open** signal blueprint,
grids the by-name axes against its `param_space()`, builds each member through cycle-1's grids the by-name axes against its `param_space()`, builds each member through cycle-1's
`wrap_stage1r` seam, and aggregates to a `FamilyKind::Sweep` family — byte-identical in shape `wrap_r` seam, and aggregates to a `FamilyKind::Sweep` family — byte-identical in shape
to the hard-wired sweep (`append_family` / `sweep_member_reports` reused verbatim). This is the to the hard-wired sweep (`append_family` / `sweep_member_reports` reused verbatim). This is the
C21 step beyond a single run: the World now constructs and orchestrates **families** of C21 step beyond a single run: the World now constructs and orchestrates **families** of
harnesses from topology-data, not just one. Each member manifest carries the **shared** harnesses from topology-data, not just one. Each member manifest carries the **shared**
@@ -1928,7 +1928,7 @@ the loaded blueprint's params over the user `--axis` grid (the #169 prefixed nam
`select_winner` reused verbatim), runs it out-of-sample, and aggregates to a `select_winner` reused verbatim), runs it out-of-sample, and aggregates to a
`FamilyKind::WalkForward` family — persisted + content-addressed + `aura reproduce` bit-identical `FamilyKind::WalkForward` family — persisted + content-addressed + `aura reproduce` bit-identical
(the read-side WalkForward branch rebuilds each OOS window from `manifest.window`). One (the read-side WalkForward branch rebuilds each OOS window from `manifest.window`). One
substitution deep from the hard-wired stage1-r WF arm (the loaded blueprint via substitution deep from the hard-wired r-sma WF arm (the loaded blueprint via
`blueprint_axis_probe` replaces the built-in strategy); a bad `--axis` is a clean in-closure `blueprint_axis_probe` replaces the built-in strategy); a bad `--axis` is a clean in-closure
exit 2, never a panic. This is Arm A (the settled direction): the loaded IS-optimizing form exit 2, never a panic. This is Arm A (the settled direction): the loaded IS-optimizing form
honestly carries the `walkforward` name. Reduce-mode members are R-measured (`oos_r` the honestly carries the `walkforward` name. Reduce-mode members are R-measured (`oos_r` the
@@ -1942,11 +1942,11 @@ prints are exactly what `--axis` binds. Every listed name is **mandatory** on a
`walkforward`: the blueprint must be fully bound before it runs, so a subset grid is refused with `walkforward`: the blueprint must be fully bound before it runs, so a subset grid is refused with
the missing knob named (`BindError::MissingKnob`) and there is no default — pin a knob you do not the missing knob named (`BindError::MissingKnob`) and there is no default — pin a knob you do not
want to vary with a single-value axis (`--axis <name>=<one-value>`). A single `blueprint_axis_probe` helper now single-sources want to vary with a single-value axis (`--axis <name>=<one-value>`). A single `blueprint_axis_probe` helper now single-sources
the wrapped probe (`wrap_stage1r(loaded_signal).param_space()`) for the sweep terminal, the MC the wrapped probe (`wrap_r(loaded_signal).param_space()`) for the sweep terminal, the MC
closed-check, AND the listing (three former inline copies → one), so **listed == swept by closed-check, AND the listing (three former inline copies → one), so **listed == swept by
construction** (and stays so across #159's harness retirement — the listing tracks whatever the construction** (and stays so across #159's harness retirement — the listing tracks whatever the
sweep actually resolves, never a second source of truth). The names are prefixed by the current sweep actually resolves, never a second source of truth). The names are prefixed by the current
stage1-r wrapping (`stage1_signal.fast.length`, the nested-composite prefix), not the raw r-sma wrapping (`sma_signal.fast.length`, the nested-composite prefix), not the raw
`param_space` — which is why the discovery lives on the sweep verb (it owns the wrapping), not `param_space` — which is why the discovery lives on the sweep verb (it owns the wrapping), not
`graph introspect`. Per-member trace-writing on `--trace` (#168) remains tracked debt. `graph introspect`. Per-member trace-writing on `--trace` (#168) remains tracked debt.
@@ -1956,7 +1956,7 @@ content-addressed blueprint store (`aura reproduce`) shipped cycle 0094 (see C18
Realization). What **remains** is a content-id that covers **structural-axis / whole-harness Realization). What **remains** is a content-id that covers **structural-axis / whole-harness
variants** (the scaffolding is not yet blueprint-data) and the debug-name-in-id question variants** (the scaffolding is not yet blueprint-data) and the debug-name-in-id question
(#171/#170). Still deferred: retiring the pre-C24 (#171/#170). Still deferred: retiring the pre-C24
hard-wired `aura-cli` harnesses (`HarnessKind`, `run_stage1_r`, `*_sweep_family`) once hard-wired `aura-cli` harnesses (`HarnessKind`, `run_r_sma`, `*_sweep_family`) once
the project-as-crate layer lands (#159, paired with #157's data-authoring surface). **Out of the first cut's round-trippable set** the project-as-crate layer lands (#159, paired with #157's data-authoring surface). **Out of the first cut's round-trippable set**
(deliberate; fails clean as `UnknownNodeType`, never a silent wrong graph): recording (deliberate; fails clean as `UnknownNodeType`, never a silent wrong graph): recording
sinks (capture an `mpsc::Sender` — runtime identity, not param-generic data, C19) and sinks (capture an `mpsc::Sender` — runtime identity, not param-generic data, C19) and
+2 -2
View File
@@ -93,7 +93,7 @@ The set of harness instances produced by varying the structural axes (strategy
### exposure stream ### exposure stream
**Avoid:** — (superseded) **Avoid:** — (superseded)
**Superseded by `bias` (2026-06 R-reframe).** The pre-reframe primary output: a signed, bounded `f64 ∈ [-1,+1]` per cycle read as the desired fractional *position* (conflating direction + conviction + size). Reframed to `bias` (unsized direction + conviction), sizing moved downstream to the `Sizer`/`RiskExecutor`; the term and the `Exposure` node persist in pre-reframe code until the Stage-1 rename. **Superseded by `bias` (2026-06 R-reframe).** The pre-reframe primary output: a signed, bounded `f64 ∈ [-1,+1]` per cycle read as the desired fractional *position* (conflating direction + conviction + size). Reframed to `bias` (unsized direction + conviction), sizing moved downstream to the `Sizer`/`RiskExecutor`; the term and the `Exposure` node persisted in pre-reframe code until the r-family rename (#174).
### firing policy ### firing policy
**Avoid:** — **Avoid:** —
@@ -249,7 +249,7 @@ System Quality Number — `√n · mean_R / stdev_R`: the dispersion- and trade-
### Stage 1 / Stage 2 ### Stage 1 / Stage 2
**Avoid:** — (superseded) **Avoid:** — (superseded)
**Superseded (2026-06-28 C10 reframe): there is no Stage-2 currency/compounding gate.** Research is one pure feed-forward R loop — gross R → net R via the `cost model` — with money, compounding, and a real broker living only at a separate later **live / deploy edge** (compounding is a post-hoc money-management transform of the net-R sequence, not an in-loop axis). The pre-reframe cleave gated a Stage-2 currency / fixed-fractional / realistic-broker layer behind `E[R] > 0`; **"Stage-1"** now survives only as a historical cycle / identifier name for the shipped feed-forward R chain, not one half of a two-stage gate. **Superseded (2026-06-28 C10 reframe): there is no Stage-2 currency/compounding gate.** Research is one pure feed-forward R loop — gross R → net R via the `cost model` — with money, compounding, and a real broker living only at a separate later **live / deploy edge** (compounding is a post-hoc money-management transform of the net-R sequence, not an in-loop axis). The pre-reframe cleave gated a Stage-2 currency / fixed-fractional / realistic-broker layer behind `E[R] > 0`; **"Stage-1"** now survives only as a historical cycle name the shipped feed-forward R chain's identifier family was renamed to the r-family (`r-sma` / `r-breakout` / `r-meanrev`, #174) — not one half of a two-stage gate.
### strategy ### strategy
**Avoid:** — **Avoid:** —