audit(0079): cycle close — drift-clean; ledger crate-topology sync + serde_json dev-dep tightening (refs #136)

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).
This commit is contained in:
2026-06-26 23:57:17 +02:00
parent 94c88eb7e1
commit a8f67c7c80
2 changed files with 35 additions and 10 deletions
+6 -2
View File
@@ -11,6 +11,10 @@ aura-core = { path = "../aura-core" }
# it gives the run-report types a typed (de)serialization path for the run # it gives the run-report types a typed (de)serialization path for the run
# registry (cycle 0029). serde's output is deterministic (C1-safe). # registry (cycle 0029). serde's output is deterministic (C1-safe).
serde = { workspace = true } 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 } serde_json = { workspace = true }
+29 -8
View File
@@ -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 half's **schema** now landed (its derivation and the brokers remain deferred): a
closed `PositionAction { Buy, Sell, Close }` enum + the `PositionEvent` row closed `PositionAction { Buy, Sell, Close }` enum + the `PositionEvent` row
(`event_ts`, `action`, `position_id`, `instrument_id`, unsigned `volume`; no (`event_ts`, `action`, `position_id`, `instrument_id`, unsigned `volume`; no
`open_ts`; direction *is* the action) live in `aura-engine` beside `RunMetrics` as a `open_ts`; direction *is* the action) live beside `RunMetrics` as a
post-run value type (not a per-`eval` node — C8). `action` serde-encodes as a bare 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 `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: requires, with an out-of-range code rejected on read. Still deferred:
`derive_position_events` (the first-difference reduction over the exposure history, `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 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], 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 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 (`risk_executor(StopRule, risk_budget)` — bias+price roles embedding
`stop-rule → Sizer → PositionManagement`, beside `vol_stop`) with a `StopRule{Fixed,Vol}` `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 **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 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 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 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 the dedicated `aura-composites` crate (the engine's convenience layer over the standard
`summarize_r`), making `aura-engine → aura-std` a normal dependency — the graph stays nodes — extracted to its own crate after this note), so `aura-engine`'s runtime
acyclic. **Stage 2** (currency P&L, fixed-fractional compounding through the z⁻¹ 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 fill-edge register, realistic brokers consuming the position-event table) remains the
deferred downstream layer, entered only after `E[R] > 0`. deferred downstream layer, entered only after `E[R] > 0`.
**Realization (cycle 0066).** SQN is now the operational single-number objective for **Realization (cycle 0066).** SQN is now the operational single-number objective for
@@ -708,7 +710,8 @@ strategies and a swept member charts (`chart <n>/<member> --tap r_equity`).
**Realization (cycle 0068, #115 — position-event derive).** The Stage-2 audit layer's **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 *derivation* now landed (the schema was 0063, #114; the realistic brokers consuming it
remain #116). `derive_position_events(record, instrument_id) -> Vec<PositionEvent>` remain #116). `derive_position_events(record, instrument_id) -> Vec<PositionEvent>`
(`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 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, 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 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 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 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 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`). spec from `aura-ingest`).
A position open at window end emits its open with **no synthetic `Close`** (the table 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). 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 **Why.** The engine/game split keeps the engine sharp and reusable while each
project versions its own research with its own forward-queue. Promotion project versions its own research with its own forward-queue. Promotion
(local → shared → std) is the ordinary Rust reuse gradient, no new mechanism. (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 ### C17 — Authoring surface
**Guarantee.** All *logic* — nodes, strategies, **and experiments/harnesses** **Guarantee.** All *logic* — nodes, strategies, **and experiments/harnesses**