From aaca18c6f6b0db6c01edb7f3025512345ec2ef29 Mon Sep 17 00:00:00 2001 From: Brummel Date: Sat, 11 Jul 2026 12:28:15 +0200 Subject: [PATCH] =?UTF-8?q?audit:=20net-r=20cycle=20close=20=E2=80=94=20le?= =?UTF-8?q?dger=20reconciled,=20drift=20items=20resolved?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Architect review over dd23ea3..HEAD (the #234/#152 cycle, 6 commits). What holds (architect-confirmed): CostSpec is a closed, deny-unknown- fields vocabulary with typed slots mirroring RiskRegime, field names conforming to the builders' ParamSpec names; cost-less docs hash byte-identically (C18); cost threads through the single MemberRunner seam so every campaign stage nets uniformly; costed families reproduce bit-identically incl. Carry; the #221-deleted leg is rebuilt optional (net = gross under the empty model); both cost_graph leaks replaced by the interned single source (#152). Resolved this close: [high] the C10 realization notes contradicted shipped reality — the two 'wired on the run path via --cost-*' claims carry superseded-annotations, the 0084 carried-debt note a discharged- annotation, and a new cycle-net-r realization documents the campaign cost block, the net-by-default decision, the manifest/reproduce round-trip, and the returned net_r_equity tap; [medium] two dangling persist_traces_r docstring references reworded to what exists; [medium] the one-knob-per-cost-node invariant behind the manifest round-trip is now unit-pinned (exactly one distinctly-named knob per shipped builder). Cycle spec and plan (git-ignored working files) discarded per convention. refs #234 --- crates/aura-cli/src/campaign_run.rs | 32 +++++++++++++++++++++++++---- docs/design/INDEX.md | 32 ++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/crates/aura-cli/src/campaign_run.rs b/crates/aura-cli/src/campaign_run.rs index 06e6a69..7de560c 100644 --- a/crates/aura-cli/src/campaign_run.rs +++ b/crates/aura-cli/src/campaign_run.rs @@ -633,7 +633,7 @@ fn present_campaign( } /// Which drained wrap-convention channel a requested tap persists from on a -/// campaign trace re-run. The routing mirrors `persist_traces_r` (main.rs): +/// campaign trace re-run. The routing follows the wrap-convention channels: /// equity <- the SimBroker equity recorder (tx_eq), exposure <- the Bias /// recorder (tx_ex), r_equity <- the cum_realized_r + unrealized_r recorder /// (tx_req), net_r_equity <- the cost leg's LinComb(4) net-curve recorder @@ -747,9 +747,8 @@ fn cell_member_fanout(cell_out: &CellOutcome) -> Vec<(Option, &RunReport /// drift alarm — manifest fields are fresh-context and not compared), and /// writes the requested-AND-producible taps through the sweep verbs' /// member-dir mechanism: a slash-joined `"{name}/{key}"` handed to -/// `TraceStore::write` (the `persist_traces_r(&format!("{name}/{key}"), ..)` -/// layout — the fn itself is not reused: it writes a fixed tap set and -/// process-exits on error). The written index manifest is the RECORDED +/// `TraceStore::write` (the slash-joined member-dir layout the sweep verbs +/// established). The written index manifest is the RECORDED /// member's — the trace documents that run's provenance, not a re-derived /// fresh-context one. Loud stderr per no-candidate cell (neither a nominee /// nor a non-empty terminal family) and ONCE per unproducible requested tap @@ -1309,4 +1308,29 @@ mod tests { ); assert!(cost_nodes_for(&[]).is_empty(), "an empty model binds no nodes"); } + + #[test] + /// #234: the manifest-stamp/reproduce round-trip (`cost[k].` -> + /// `cost_specs_from_params`) reconstructs a component from its knob NAME — + /// sound only while every shipped cost node declares exactly one, + /// distinctly-named knob. A second knob (or a name collision) would break + /// variant reconstruction with no compiler error; this pin makes it loud. + fn every_cost_builder_declares_exactly_one_distinct_knob() { + let knobs: Vec = [ + aura_std::ConstantCost::builder(), + aura_std::VolSlippageCost::builder(), + aura_std::CarryCost::builder(), + ] + .iter() + .map(|b| { + let params = b.params(); + assert_eq!(params.len(), 1, "one knob per cost node: {}", b.label()); + params[0].name.clone() + }) + .collect(); + let mut dedup = knobs.clone(); + dedup.sort(); + dedup.dedup(); + assert_eq!(dedup.len(), knobs.len(), "knob names must be pairwise distinct: {knobs:?}"); + } } diff --git a/docs/design/INDEX.md b/docs/design/INDEX.md index 2a42a57..110341a 100644 --- a/docs/design/INDEX.md +++ b/docs/design/INDEX.md @@ -741,7 +741,10 @@ cost = 0 baseline on an empty stream. The headline sink is **`net_r_equity`** = (C8/C18), a sibling of `r_equity`, emitted only when a cost is authored. Wired on the **run path** via `--cost-per-trade` (`r_sma_graph`); sweep / walk-forward / mc pass `None` (cost on the reduce-mode sweep path and cost in OOS pooling deferred — the -positional join holds only while one cost node fires in lockstep with PM). Deferred to +positional join holds only while one cost node fires in lockstep with PM). +[Superseded 2026-07-11 (#234): the `--cost-*` run-path flags are gone (#221 removed +that surface); cost is authored as the campaign document's `cost` block and reaches +every family/campaign member — see the cycle-net-r realization below.] Deferred to later milestone cycles: the general `CostNode` trait + multi-node cost-graph composite-builder, data-grounded factors (realized-vol → slippage, recorded-rate → swap), per-cycle-held accrual (carry / funding), and the conviction-weighting @@ -770,6 +773,8 @@ composable with `--cost-per-trade` (their costs sum); sweep / walk-forward / mc `None`. The 3-field cost triple is now restated by-convention across the two producers + `CostSum` (a compiler-unlinked lockstep) — to be unified by the still-deferred general `CostNode` trait (now justified by two concrete nodes). Decision log: #148. +[Superseded 2026-07-11 (#234): the `--slip-vol-mult`/`--cost-per-trade` flags are gone; +authoring moved to the campaign `cost` block — see the cycle-net-r realization below.] **Realization (cycle 0083 — CostNode trait + shared cost-record contract, cycle 3, #148).** The deferred unifier ships. A new `aura-std/src/cost.rs` owns the cost-model @@ -811,6 +816,8 @@ non-load-bearing). **Carried debt (#152, for the deferred sweep-cost cycle):** t `cost_graph` `.leak()`s runtime port names per build — fine for one-shot run-path construction, but to be interned (the `COL_PORTS` production pattern, not the test-only `.leak()`) before cost reaches the per-member sweep path. Decision log: #148. +[Discharged 2026-07-11 (#152/#234): `cost_port`/`intern_port` in `aura-std/src/cost.rs` +are the process-global interned single source; both `cost_graph` `.leak()`s are gone.] **Realization (cycle 0085 — per-cycle-held accrual, cycle 5, #148).** C10's "per-trade factors deduct at close; **per-cycle-held factors accrue over the hold**" @@ -844,6 +851,29 @@ integration test. **Deferred (each its own #148 cycle):** a notional-based carry (rollover-boundary timing, 3× Wednesday, long/short asymmetry — deploy-edge realism), and sweep-path cost. Decision log: #148. +**Realization (cycle net-r — cost reaches the family/campaign path, #234/#152, +2026-07-11).** The deferred sweep-path cost ships, delivered where C24 put experiment +intent: the campaign document gains an additive `cost: Vec` block — a closed, +externally tagged vocabulary over the three shipped nodes (`constant` / `vol_slippage` +/ `carry`, field names = the builders' own `ParamSpec` names), mirroring the `risk` +block (serde `default` + skip-if-empty: cost-less docs hash byte-identically, C18). +Net is the **default**: an absent block is the explicit zero-cost model and +`summarize_r`'s net family equals gross under it — every result is net, no second +"gross-labelled" result kind. `cost_nodes_for` (beside `stop_rule_for_regime`) is the +one doc→builder binding, every component fully bound (the wrapped param space stays +cost-invariant); `wrap_r` carries an optional cost leg (the #221-deleted wiring +rebuilt: cost_graph off the executor's four geometry outputs, the vol proxy back in +production, a gated cost recorder in reduce mode as the `summarize_r` join input, the +`LinComb(4)` `net_r_equity` curve in trace mode). Member manifests stamp the +components; both re-run sides re-derive them (reproduce via `cost_specs_from_params` — +the #233 stop pattern — and the persist re-run binds the same model, so the C1 drift +alarm compares like with like; costed families reproduce bit-identically, pinned incl. +`Carry`). `TapChannel::Net` routes `net_r_equity` to persisted curves; a cost-less doc +requesting it keeps a remedy-naming skip notice. The `--cost-*` run-path flags do not +return; the verb sugar passes no cost (docs carry it) — the same lean-flag call as the +C26 bindings block. #152's interning ships with it (`cost_port`/`intern_port`, both +`cost_graph` `.leak()`s gone). Decision log: #234. + **Realization (cost-flag harness scoping, #153).** Cost flags are defined only against an R-evaluator harness (the gross-R → net-R chain). On a non-R harness (`--harness sma`/`macd`, which produce no R) a cost flag is a **usage error