feat: measurement run verb — shape-dispatch aura run on blueprint output

Phase 3 of the Stratification milestone (contract C28). `aura run` now
shape-dispatches on the loaded blueprint: a `bias` output takes the
existing strategy path (wrap_r + R evaluation), byte-identical; a
blueprint that declares taps but exposes no `bias` runs bare via the new
`run_measurement` (bootstrap, run, drain the declared taps) with no
SimBroker, no risk executor, and no per-cycle equity/exposure/r
recorders; a blueprint exposing neither is refused. This gives the
measurement layer its own run verb and removes the O(cycles) recording
retention (the measured ~2 GB peak RSS over 12y UK100 M1) the mandatory
R scaffold imposed on a measurement-shaped run.

A new `MeasurementReport { manifest, taps }` (aura-engine::report, beside
RunReport) is the stdout/record shape — no R metrics, since a bare run
has no broker. The manifest's `broker` carries an interim "measurement"
sentinel; the honest `Option<String>` model is deferred to the #147
metric-genericity block, keeping this phase #147-free and the
strategy-path C18 goldens byte-identical.

`run_signal_r` is untouched (the tap machinery is duplicated into
`run_measurement`, not extracted). The measurement arm's closed-blueprint
guard uses `signal.param_space()` rather than `blueprint_axis_probe`,
which welds wrap_r and would panic on a no-`bias` signal.

refs #286
This commit is contained in:
2026-07-19 16:09:51 +02:00
parent 1c49d5dce2
commit 34ff539143
4 changed files with 236 additions and 40 deletions
+2 -2
View File
@@ -71,8 +71,8 @@ pub use harness::{
pub use report::{
derive_position_events, expected_max_of_normals, f64_field, inv_norm_cdf, join_on_ts,
r_metrics_from_rs, summarize, summarize_r, ColumnarTrace, FamilySelection, JoinedRow,
PositionAction, PositionEvent, ProjectProvenance, RMetrics, RunManifest, RunMetrics,
RunReport, SelectionMode,
MeasurementReport, PositionAction, PositionEvent, ProjectProvenance, RMetrics, RunManifest,
RunMetrics, RunReport, SelectionMode,
};
pub use sweep::{
sweep, GridSpace, ListSpace, ParamRange, RandomSpace, Space, SweepError, SweepFamily,
+17
View File
@@ -111,6 +111,23 @@ impl RunReport {
}
}
/// A measurement run's stdout/record shape (C28 phase 3): the run descriptor plus
/// the declared tap names, mirroring the persisted `index.json`. A measurement run
/// has no broker → no equity/exposure → NO R `metrics` (the difference from
/// [`RunReport`]). The tapped series themselves persist through the trace store.
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct MeasurementReport {
pub manifest: RunManifest,
pub taps: Vec<String>,
}
impl MeasurementReport {
/// Canonical machine-readable JSON via serde, same encoder as [`RunReport`].
pub fn to_json(&self) -> String {
serde_json::to_string(self).expect("a finite MeasurementReport always serializes")
}
}
/// Reduce a run's recorded pip-equity + exposure streams into summary metrics.
/// Pure — identical inputs yield identical metrics (C1/C12). Timestamps are
/// carried in the input to match exactly what a sink records; the reduction