From a8f67c7c803699dfb2c82711879d5cdf0ee486b8 Mon Sep 17 00:00:00 2001 From: Brummel Date: Fri, 26 Jun 2026 23:57:17 +0200 Subject: [PATCH] =?UTF-8?q?audit(0079):=20cycle=20close=20=E2=80=94=20drif?= =?UTF-8?q?t-clean;=20ledger=20crate-topology=20sync=20+=20serde=5Fjson=20?= =?UTF-8?q?dev-dep=20tightening=20(refs=20#136)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Architect drift review (3e8fd73..HEAD): the extraction is faithful and behaviour-preserving — every moved symbol's definition left report.rs, 35 unit tests relocated with their subject, aura-engine/src/lib.rs byte-unchanged, the four downstream crates byte-unchanged. No code defect. Regression gate green: cargo build/test --workspace = 665 passed / 0 failed (incl. the C18 cli_run goldens and the registry legacy-line loads); clippy --workspace --all-targets -D warnings clean. Ledger sync (docs/design/INDEX.md) — pre-existing crate-topology drift the extraction made visible: - C10 notes (cycles 0063/0068) placed PositionAction / PositionEvent / derive_position_events "in aura-engine beside RunMetrics / summarize_r"; cycle 0079 moved all of them to aura-analysis — corrected, with a forward pointer to the new C16 note. - C10 cycle-0065 note claimed "RiskExecutor ships as a public aura-engine composite-builder" and "Composites live in aura-engine ... aura-engine -> aura-std a normal dependency"; both stale — composites live in aura-composites and aura-std is now an aura-engine [dev-dependencies] only. Corrected. - Added a C16 realization note recording the aura-analysis extraction as an architecture fact: the engine<->domain seam, the current five non-node workspace crates, and the deferred #136 generic-over-M tail. serde_json moved from [dependencies] to [dev-dependencies] in aura-analysis (architect debt item): the production types derive serde, but JSON rendering lives in aura-engine's RunReport::to_json, so serde_json is test-only here and must not enter the frozen artifact's library link (C16 frozen-artifact scrutiny). Build + the 35 crate tests stay green after the move. No baseline moved (the suite is the regression gate; it stayed green), so no ratify statement. No spec/plan ephemera to drop (compiler-driven / agent-driven cycle — no docs/specs or docs/plans files were created). --- crates/aura-analysis/Cargo.toml | 8 +++++-- docs/design/INDEX.md | 37 ++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 10 deletions(-) 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** —