plan: 0069 exposure->bias rename tail (#126)
The deferred rename of the strategy-output residue (the typed exposure_sign_flips
metric with serde back-compat, the registry rank vocab, the exposure_scale manifest
label, the .named("exposure") Bias instances), keeping SimBroker's pre-reframe
exposure concept and the on-disk exposure tap label (scope fork on #117).
refs #126 #117
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
# exposure→bias rename tail (cycle 0065, #126) — Implementation Plan
|
||||
|
||||
> **Parent spec:** `docs/specs/0065-stage1-r-signal-quality.md` (secondary change (a),
|
||||
> deferred from iter-0; scope fork decided on #117).
|
||||
>
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: use the `implement` skill to run this
|
||||
> plan. Steps use `- [ ]` checkboxes.
|
||||
|
||||
**Goal:** complete the `exposure → bias` rename of the strategy-output residue — the
|
||||
typed `exposure_sign_flips` metric (with serde back-compat), the registry rank vocab,
|
||||
the `exposure_scale` manifest label, and the `.named("exposure")` Bias instances —
|
||||
**without** touching SimBroker's deliberate pre-reframe exposure concept or the on-disk
|
||||
`exposure` tap label (fork on #117).
|
||||
|
||||
**Architecture:** `RunMetrics.exposure_sign_flips` is a serde struct field, so its
|
||||
rename is one atomic compile-unit across all crates; a `#[serde(alias =
|
||||
"exposure_sign_flips")]` keeps legacy `runs.jsonl` readable while new output uses
|
||||
`bias_sign_flips`. The registry rank metric accepts BOTH strings (CLI back-compat).
|
||||
The manifest param label and Bias instance names are plain string renames.
|
||||
|
||||
**Test split rule (load-bearing — apply everywhere):** a test that pins **new output**
|
||||
(serialize / byte-pin / a freshly-built `RunMetrics`) updates to `bias_sign_flips` /
|
||||
`bias_scale`; a test that reads a **legacy** line (deserialize back-compat) KEEPS the
|
||||
old `exposure_sign_flips` / `exposure_scale` key (it now pins the serde alias).
|
||||
|
||||
**Files this plan modifies** (site map from grep — the compiler enumerates the field-rename sites):
|
||||
- Modify: `crates/aura-engine/src/report.rs` — `RunMetrics.exposure_sign_flips` field (+ alias), the `summarize` fold, the constructor, tests.
|
||||
- Modify: `crates/aura-engine/src/mc.rs:40,88,211` — the parallel `exposure_sign_flips` aggregate field.
|
||||
- Modify: `crates/aura-registry/src/lib.rs` — `Metric::ExposureSignFlips` vocab, `metric_cmp`, the rank-string match (+both), the error message, constructor, tests.
|
||||
- Modify: `crates/aura-cli/src/main.rs` — the `:1462` JSON output key, the `exposure_scale` manifest builders (`:557,594,1439,1675,1825,2400`), the `.named("exposure")` instances (`:950,1042,1601,1738`), tests (`:2473,2560,2728`).
|
||||
- Modify: `crates/aura-cli/tests/cli_run.rs:39` — the byte-pin.
|
||||
- Modify: `crates/aura-engine/src/test_fixtures.rs:65`, `crates/aura-engine/src/blueprint.rs:2566` — `.named("exposure")`.
|
||||
- Modify: `crates/aura-engine/tests/risk_executor.rs:142`, `crates/aura-ingest/tests/{real_bars,streaming_seam}.rs`, `crates/aura-ingest/examples/ger40_breakout_{compare,real,sweep}.rs` — field/label references.
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Rename `exposure_sign_flips → bias_sign_flips` (typed field + serde alias + rank vocab)
|
||||
|
||||
**Files:** `report.rs`, `mc.rs`, `aura-registry/src/lib.rs`, `main.rs`, `cli_run.rs`, `risk_executor.rs`, the three ger40 examples — every `exposure_sign_flips` site (the compiler flags them once the field is renamed).
|
||||
|
||||
- [ ] **Step 1: Write the RED back-compat test** (in `crates/aura-engine/src/report.rs` tests)
|
||||
|
||||
```rust
|
||||
#[test]
|
||||
fn legacy_exposure_sign_flips_key_reads_via_alias_and_new_output_uses_bias() {
|
||||
// a legacy runs.jsonl metrics line carries the OLD key — the serde alias must accept it.
|
||||
let legacy = r#"{"total_pips":12.0,"max_drawdown":1.0,"exposure_sign_flips":3}"#;
|
||||
let m: RunMetrics = serde_json::from_str(legacy).expect("legacy exposure_sign_flips deserialises");
|
||||
assert_eq!(m.bias_sign_flips, 3);
|
||||
// new output serialises the NEW key, and the old key is gone from output.
|
||||
let json = serde_json::to_string(&m).unwrap();
|
||||
assert!(json.contains("\"bias_sign_flips\":3"), "new output uses bias_sign_flips: {json}");
|
||||
assert!(!json.contains("exposure_sign_flips"), "old key absent from new output: {json}");
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run it to confirm RED**
|
||||
|
||||
Run: `cargo test -p aura-engine legacy_exposure_sign_flips_key_reads_via_alias`
|
||||
Expected: FAIL — does not compile (`m.bias_sign_flips` does not exist yet).
|
||||
|
||||
- [ ] **Step 3: Rename the field + add the serde alias** (`crates/aura-engine/src/report.rs`)
|
||||
|
||||
In the `RunMetrics` struct (`:29`):
|
||||
```rust
|
||||
#[serde(alias = "exposure_sign_flips")]
|
||||
pub bias_sign_flips: u64,
|
||||
```
|
||||
Rename the fold local (`:310,317`) `exposure_sign_flips` → `bias_sign_flips` and the constructor (`:322`) `RunMetrics { total_pips, max_drawdown, bias_sign_flips, r: None }`. Update the in-file tests per the **test split rule**: assertions on a freshly-summarised `m` (`:818,842,849`) and serialize round-trips (`:888,913,933,947,979`) → `bias_sign_flips`; the **legacy-deserialise** test (`:971`) KEEPS `exposure_sign_flips` in its JSON literal (it now pins the alias) and updates only its assertion field to `m.bias_sign_flips`.
|
||||
|
||||
- [ ] **Step 4: Propagate the field rename across the workspace**
|
||||
|
||||
Rename `exposure_sign_flips` → `bias_sign_flips` at every other site the compiler flags:
|
||||
- `crates/aura-engine/src/mc.rs:40,88,211` — the aggregate field + its `pick`/constructor uses (if the MC aggregate serialises to `families.jsonl`, add the same `#[serde(alias = "exposure_sign_flips")]` on its field).
|
||||
- `crates/aura-cli/src/main.rs:1462` (`"bias_sign_flips": agg.bias_sign_flips`), `:2560` (field ref); the byte-pin tests `:2473` (`"bias_sign_flips":`), `:2728` (assertion) → new key.
|
||||
- `crates/aura-cli/tests/cli_run.rs:39` → `line.ends_with("\"bias_sign_flips\":1}}")` (new output).
|
||||
- `crates/aura-engine/tests/risk_executor.rs:142`, the ger40 examples (`compare:130,154`, `real:89`, `sweep:149`) → field refs.
|
||||
|
||||
- [ ] **Step 5: Registry rank vocab — rename the variant, accept BOTH strings** (`crates/aura-registry/src/lib.rs`)
|
||||
|
||||
```rust
|
||||
// the Metric enum variant (:103):
|
||||
BiasSignFlips,
|
||||
// rank-string match (:120-122) — accept the new name AND the old one (CLI back-compat):
|
||||
"bias_sign_flips" | "exposure_sign_flips" => Metric::BiasSignFlips,
|
||||
// metric_cmp arm (:130-131):
|
||||
Metric::BiasSignFlips => a.metrics.bias_sign_flips.cmp(&b.metrics.bias_sign_flips),
|
||||
// error/listing (:185): "(known: total_pips, max_drawdown, bias_sign_flips)"
|
||||
// constructor (:214): bias_sign_flips: flips
|
||||
```
|
||||
Per the test split: the legacy-deserialise test (`:275`) KEEPS `exposure_sign_flips` in its JSON literal; the `rank_by` tests (`:296,297`) update the assertion field to `bias_sign_flips` (rank string may use either — keep `"exposure_sign_flips"` at `:296` to exercise the alias, or add a sibling asserting `"bias_sign_flips"` ranks identically).
|
||||
|
||||
- [ ] **Step 6: Build + the RED test now GREEN + suite green**
|
||||
|
||||
Run: `cargo test -p aura-engine legacy_exposure_sign_flips_key_reads_via_alias`
|
||||
Expected: PASS.
|
||||
|
||||
Run: `cargo test --workspace`
|
||||
Expected: all green (the renamed field + alias; legacy-read tests pin the alias; new-output tests pin `bias_sign_flips`).
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Rename the `exposure_scale` manifest label + `.named("exposure")` Bias instances
|
||||
|
||||
**Files:** `main.rs` (manifest builders + Bias instances), `report.rs` (manifest-builder tests), `test_fixtures.rs`, `blueprint.rs`, `real_bars.rs`, `streaming_seam.rs`.
|
||||
|
||||
- [ ] **Step 1: Rename the `.named("exposure")` Bias instances → `.named("bias")`**
|
||||
|
||||
At `crates/aura-cli/src/main.rs:950,1042,1601,1738`, `crates/aura-engine/src/test_fixtures.rs:65`, `crates/aura-engine/src/blueprint.rs:2566`: `Bias::builder().named("exposure")` → `Bias::builder().named("bias")`. (Pure debug symbols — no behaviour change. If any test asserts the node label/name `"exposure"`, update that assertion to `"bias"`; if a graph-render fixture pins the name, update it.)
|
||||
|
||||
- [ ] **Step 2: Rename the `exposure_scale` manifest param label → `bias_scale`**
|
||||
|
||||
In every manifest **builder** (new output): `crates/aura-cli/src/main.rs:557,594,1439,1675,1825,2400`, `crates/aura-engine/src/report.rs:637,879,907,927`, `crates/aura-ingest/tests/real_bars.rs:87`, `crates/aura-ingest/tests/streaming_seam.rs:87` → `("bias_scale".to_string(), Scalar::f64(...))`. Per the test split: the **legacy-deserialise** JSON literal at `report.rs:894` KEEPS `exposure_scale` (it reads an old manifest; manifest params are an untyped `Vec<(String, Scalar)>`, so no serde alias is needed — old data just carries the old label).
|
||||
|
||||
- [ ] **Step 3: Build + suite green + lint**
|
||||
|
||||
Run: `cargo build --workspace`
|
||||
Expected: clean.
|
||||
|
||||
Run: `cargo test --workspace`
|
||||
Expected: all green.
|
||||
|
||||
Run: `cargo clippy --workspace --all-targets -- -D warnings`
|
||||
Expected: clean.
|
||||
Reference in New Issue
Block a user