diff --git a/crates/aura-analysis/Cargo.toml b/crates/aura-analysis/Cargo.toml index 739802b..6556e32 100644 --- a/crates/aura-analysis/Cargo.toml +++ b/crates/aura-analysis/Cargo.toml @@ -11,6 +11,10 @@ aura-core = { path = "../aura-core" } # it gives the run-report types a typed (de)serialization path for the run # registry (cycle 0029). serde's output is deterministic (C1-safe). 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). + +[dev-dependencies] +# serde_json is used only by this crate's round-trip tests (the production types +# derive serde, but JSON rendering lives in aura-engine's RunReport::to_json). +# Test-only -> dev-dependencies, so it never enters the frozen artifact's library +# link (C16 frozen-artifact scrutiny). serde_json = { workspace = true } diff --git a/docs/design/INDEX.md b/docs/design/INDEX.md index d999c07..85f1012 100644 --- a/docs/design/INDEX.md +++ b/docs/design/INDEX.md @@ -622,8 +622,9 @@ redundant for the real path. Refuse-don't-guess on absent geometry.) half's **schema** now landed (its derivation and the brokers remain deferred): a closed `PositionAction { Buy, Sell, Close }` enum + the `PositionEvent` row (`event_ts`, `action`, `position_id`, `instrument_id`, unsigned `volume`; no -`open_ts`; direction *is* the action) live in `aura-engine` beside `RunMetrics` as a -post-run value type (not a per-`eval` node — C8). `action` serde-encodes as a bare +`open_ts`; direction *is* the action) live beside `RunMetrics` as a +post-run value type (not a per-`eval` node — C8) — both in the `aura-analysis` crate +since cycle 0079 (#136); originally `aura-engine`, see the C16 cycle-0079 note. `action` serde-encodes as a bare `i64` (Buy=0, Sell=1, Close=2), the C7 scalar column form the ledger's table spec requires, with an out-of-range code rejected on read. Still deferred: `derive_position_events` (the first-difference reduction over the exposure history, @@ -656,7 +657,7 @@ the feed-forward sizing seam, and **R is size-invariant** — scaling `risk_budg leaves every `realized_r` unchanged (pinned by a RED test). **`summarize_r`** is a post-run fold (sibling of `summarize`, **not** an in-graph node) → `RMetrics` (E[R], SQN, win-rate, profit-factor, max-R-drawdown, conviction terciles, net-of-cost). The -**RiskExecutor** ships as a public `aura-engine` composite-builder +**RiskExecutor** ships as a public `aura-composites` composite-builder (`risk_executor(StopRule, risk_budget)` — bias+price roles embedding `stop-rule → Sizer → PositionManagement`, beside `vol_stop`) with a `StopRule{Fixed,Vol}` **structural axis** (C11); the **Veto** stays a documented seam, not a runtime node (a @@ -670,9 +671,10 @@ both `SimBroker` (pip) and the RiskExecutor (R) for an honest dual yardstick; an The R-record redundancy `debug_assert` uses a **scale-robust relative tolerance** (an absolute `1e-9` panicked on tiny-pip FX where the `entry − stop` denominator reconstruction loses precision; the stored `realized_r` is exact). Composites live in -`aura-engine` (the engine's convenience layer over the standard nodes, sibling of -`summarize_r`), making `aura-engine → aura-std` a normal dependency — the graph stays -acyclic. **Stage 2** (currency P&L, fixed-fractional compounding through the z⁻¹ +the dedicated `aura-composites` crate (the engine's convenience layer over the standard +nodes — extracted to its own crate after this note), so `aura-engine`'s runtime +dependency stays `aura-core`-only and `aura-std` is now an `aura-engine` +`[dev-dependencies]` — the graph stays acyclic. **Stage 2** (currency P&L, fixed-fractional compounding through the z⁻¹ fill-edge register, realistic brokers consuming the position-event table) remains the deferred downstream layer, entered only after `E[R] > 0`. **Realization (cycle 0066).** SQN is now the operational single-number objective for @@ -708,7 +710,8 @@ strategies and a swept member charts (`chart / --tap r_equity`). **Realization (cycle 0068, #115 — position-event derive).** The Stage-2 audit layer's *derivation* now landed (the schema was 0063, #114; the realistic brokers consuming it remain #116). `derive_position_events(record, instrument_id) -> Vec` -(`aura-engine`, beside `summarize_r`) is the **first difference of the executed book**: +(in `aura-analysis` since cycle 0079, #136; originally `aura-engine`, beside +`summarize_r`) is the **first difference of the executed book**: a pure post-run reduction over the `PositionManagement` dense record (read positionally as type-erased `Scalar`s, C7 SoA — no in-graph node, so the hot path stays domain-free, C14), emitting a `Buy`/`Sell` at each open and a `Close` at each exit, a reversal (or a @@ -717,7 +720,7 @@ stop-then-same-cycle reopen) emitting **Close then the opposite open at one `eve table, not a per-`eval` output). The close sizes the **actual book** (the closed position's stored volume), never an exposure delta — the post-reframe replacement for the rolled-back 0064 exposure-integral derive (#117). `instrument_id` is a caller-supplied -scalar (`aura-engine` depends only on `aura-core`, so it never imports an instrument +scalar (`aura-analysis` depends only on `aura-core`, so it never imports an instrument spec from `aura-ingest`). A position open at window end emits its open with **no synthetic `Close`** (the table records actual executed events; `summarize_r`'s force-close is for the R metric only). @@ -880,6 +883,24 @@ above, not a blanket ban.) **Why.** The engine/game split keeps the engine sharp and reusable while each project versions its own research with its own forward-queue. Promotion (local → shared → std) is the ordinary Rust reuse gradient, no new mechanism. +**Realization (cycle 0079, #136 — the analysis leaf becomes its own crate).** The +trading-domain **analysis** layer — the post-run reductions that are pure functions of a +run's recorded data (`RunMetrics`, `RMetrics`, the R reduction `summarize_r` / +`r_metrics_from_rs`, the position-event table `PositionEvent` / `PositionAction` / +`derive_position_events`, and the multiple-comparison hurdle math `inv_norm_cdf` / +`expected_max_of_normals`) — is extracted out of `aura-engine`'s `report` module into a +dedicated **`aura-analysis`** crate (deps: `aura-core` + `serde` only; `serde_json` is a +dev-dependency, test-only). This sharpens the engine↔domain seam: the run loop's crate no +longer *defines* the trading metrics — it `pub use`-re-exports them for source +compatibility this cycle, and still hosts the trace-coupled `summarize`, `RunReport`, +`RunManifest`, and the columnar trace utils. The non-node engine-workspace crates are now +`aura-engine` (run loop), `aura-cli` (`aura` binary), `aura-ingest` (ingestion edge), +`aura-composites` (composite-builder convenience layer), and `aura-analysis` (post-run +domain reductions). Behaviour-preserving (C1): every serde shape is byte-identical (C18 +goldens green). **Deferred (#136 tail, genuine design forks):** making `RunReport` +generic over the metric type and re-bounding `sweep`/`mc`/`walkforward`; relocating the +`aura-registry` `Metric`/`metric_cmp`/rank vocabulary; relocating the mis-placed +orchestration types `FamilySelection`/`SelectionMode` out of the analysis module. ### C17 — Authoring surface **Guarantee.** All *logic* — nodes, strategies, **and experiments/harnesses** —