The single-file ledger had grown to 2968 lines / ~42k tokens, mixing current design law with accreted history: 59 cycle-stamped realization blocks, 18 [HISTORY] passages, 22 supersession markers, and the C10 / C22 / C24 reframe sagas layered several supersessions deep. A code-grounding audit (31 agents, adversarially verified) confirmed 11 defects stated as current truth: stale crate homes from the C28 #288 roster split (cost nodes, PositionManagement, PositionEvent, Session), the renamed InputSpec->PortSpec, the pre-#241 project model in C16 and the open-threads section, a stale HarnessKind retirement deferral in C24, and three C28-internal inconsistencies. New shape, per the ailang precedent: - INDEX.md stays the sole addressable entry point: foundation, external components, a C-id-keyed contract map (one line per contract), and only the genuinely open architectural threads. - contracts/cNN-<slug>.md carries each contract's current truth only: Guarantee / Forbids / Why with ratified refinements integrated, plus a code-anchored Current state. All confirmed defects are fixed here; crate anchors were re-verified against the tree. - contracts/cNN-<slug>.history.md (18 sidecars) and INDEX.history.md preserve every superseded block verbatim, stamps and issue refs intact, under a frozen-record banner. Nothing was deleted: superseded design intent remains an addressable working-tree artifact, off the per-cycle audit walk. - Ledger discipline is now stated in INDEX.md: live files are edited in place at cycle close, superseded text moves verbatim to the sidecar, and a supersession marker in a live file is itself an audit finding. Every contract file was verified against its old text by an independent zero-loss pass (statement-by-statement) plus a code-accuracy spot check; C-ids and contract titles are unchanged, so existing C-id citations in code, tests, and issues resolve as before.
28 KiB
C10 — Strategy output is a bias stream; signal quality is measured in R; cost is a composable downstream graph (gross R → net R); money is decoupled to the live deploy edge: history
FROZEN HISTORICAL RECORD. Each block below was true as of its cycle/date stamp and may be superseded; this file is NOT current truth and NOT a grounding surface. Current contract: c10-bias-r-cost.md.
Realization (cycle 0007). [HISTORY — pre-reframe; SimBroker is now legacy per
the 2026-06-28 reframe.] The signal-quality half was realized at the substrate as
two aura-std nodes on the unchanged engine (the engine stays domain-free — it
routes only f64 records). The exposure stream was realized as Exposure { scale }: clamp(signal / scale, -1, +1), one f64 per fired cycle. The
sim-optimal broker was realized as SimBroker { pip_size }: a two-input node
(exposure, price) accumulating prev_exposure · (price − prev_price) / pip_size
and emitting cumulative pip equity — the exposure held into a cycle (decided at
t-1) earns that cycle's return (causal, C2); pip_size is held reference metadata
(C7/C15). An end-to-end harness (SMA-cross → Exposure → SimBroker → recording
sink) produced a recorded pip-equity curve, bit-identical across runs (C1).
Realization (per-instrument pip channel, 2026-06, #22). [HISTORY — pertains to
the legacy SimBroker pip channel.] SimBroker's pip_size is sourced per
instrument from the recorded geometry sidecar (instrument_geometry, over
data-server's symbol_meta), at the ingestion / source edge (never Aura.toml,
never Ctx); the engine stays domain-free. (The original cycle-0022 form was a
Rust-authored vetted floor InstrumentSpec { pip_size } + instrument_spec(symbol);
cycle 0074 removed that floor — see the C15 note — once the sidecar geometry made it
redundant for the real path. Refuse-don't-guess on absent geometry.) The honesty
rule is refuse, don't guess: a real-data run for a symbol with no vetted spec
is a usage error (exit 2). Threaded through the CLI aura run --real path; the
manifest broker label records the looked-up pip.
Realization (position-event schema, cycle 0063, #114). [Survives as the
deploy / reconciliation schema per the 2026-06-28 reframe — no longer a
broker-input research artifact.] 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
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, with an out-of-range code rejected on read.
The table stays broker-independent.
Reframe (2026-06-23, #117 — exposure → bias, R as the signal-quality unit).
[HISTORY — its R spine survives into the 2026-06-28 contract; its Stage-2 currency /
realistic-broker / register / flat-1R-vs-compounding portions are SUPERSEDED by that
reframe.] The contract was reframed from exposure (a signed fractional
position) to an unsized bias plus R as the signal-quality unit. The
realization notes below describe the pre-reframe code (Exposure, SimBroker,
pip-equity), retained as history and as the ancestor of the current chain:
Exposure { scale } is the ancestor of the unsized bias node, and SimBroker's
pip integral is the ancestor of the R-evaluator (which additionally requires a
stop, since R is stop-defined). The exposure → bias rename, the RiskExecutor /
Sizer / Veto nodes, and the R-evaluator landed in cycle 0065; the
position-event schema (0063, #114) survives as the audit layer. Industry grounding
for this reframe: LEAN / nautilus_trader / backtrader / QSTrader / vectorbt /
zipline (see #117 decision log).
Realization (cycle 0065 — Stage-1 R signal quality, #119/#126/#127/#128/#129).
[The R spine here is the live model; the Stage-2 deferral clause at its end is
SUPERSEDED by the 2026-06-28 reframe — there is no Stage-2 currency / compounding
layer; cost is now the cost-model graph and money lives only at the live deploy
edge. "Stage-1" reads as a historical identifier, not a gate half.] Exposure → Bias renamed the unsized strategy output (node + output field); the persisted
exposure_sign_flips metric key (serde alias), the SimBroker exposure input
slot, and the on-disk exposure tap label retain the old name. The
strategy-output param namespace (the Bias instance, its bias.scale knob,
the bias_scale manifest param) was completed in #134. A stop-rule defines 1R:
FixedStop (a triggered-constant primitive) and a vol_stop(length, k)
composition k·√EMA(Δ²) (a composition of Mul / Sqrt primitives).
PositionManagement (aura-std) is the stateful heart: it latches the
entry-cycle stop distance as the immutable R-denominator, marks against the
one-cycle-lagged fill (no look-ahead, C2), and emits a dense 14-column per-cycle
R-record (one row per eval, C8; the trade ledger is the closed_this_cycle
subset, the R-equity is cum_realized_r + unrealized_r). The Sizer (size = risk_budget / stop_distance, flat-1R) was the feed-forward sizing seam, and R is
size-invariant — scaling risk_budget leaves every realized_r unchanged
(pinned by a RED test); per the 2026-06-28 reframe the Sizer and size / volume
are removed from research (size is a deploy concept). 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);
per the 2026-06-28 reframe it folds the cost-model's net-R rather than recomputing
a scalar cost. The RiskExecutor ships as a public aura-composites
composite-builder (risk_executor(StopRule, risk_budget)) with a
StopRule{Fixed,Vol} structural axis (C20); per the 2026-06-28 reframe its
Sizer interior and risk_budget arg are dropped / vestigial, the Veto stays a
documented seam, not a runtime node. The layer is operable from the CLI: aura run --harness <sma|macd|r-sma> — a compile-time selector over Rust-authored
harnesses (C9/C17) — folds summarize_r into RunMetrics.r, the r-sma
harness fanning one bias into both SimBroker (legacy pip) and the RiskExecutor
(R); an r_equity tap charts the by-trade R-equity. Composites live in the
dedicated aura-composites crate, so aura-engine's runtime dependency stays
aura-core-only and aura-std is an aura-engine [dev-dependencies] (the graph
stays acyclic).
[HISTORY — the built-in --harness selector was retired with the demos →
blueprint-data (#159, cuts 1b-4); run is now blueprint-driven — aura run <blueprint.json> over examples/r_*.json.]
Realization (cycle 0066). SQN is the operational single-number objective for
ranking an r-sma sweep family by signal quality — C12 axis-2 (argmax-metric)
over the C18 family store. metric_cmp (aura-registry) learns the
higher-is-better R metrics sqn, expectancy_r, net_expectancy_r; a member
with no r block sorts last (NEG_INFINITY). aura sweep --strategy r-sma
produces the rankable R family, each member folding summarize_r into
RunMetrics.r. The default grid varies only the signal (fast / slow SMA
lengths), holding the stop and sizing fixed: risk_budget is R-invariant and
bias.scale is sign-only under flat-1R, and — load-bearing — the stop defines
1R, so varying it would change what R means per member and break cross-member
SQN comparability. Each swept member's manifest records the fixed R-defining params
beside the floated knobs (reproducible from its own manifest, C18).
[HISTORY — the built-in --strategy sweep surface was retired with the demos →
blueprint-data (#159, cuts 1b-4); the rankable R sweep now runs as aura sweep <blueprint.json> --axis … over r_sma_open.json (an example then; relocated to
crates/aura-cli/tests/fixtures/ by #248).]
Realization (cycle 0067, #130 + #135). #130 (SQN100): RMetrics gains
sqn_normalized = (mean_R/stdev_R)·√(min(n, 100)) — the n-normalized "SQN score"
(SQN_CAP = 100), turnover-robust where raw sqn rewards trade count. It is an
opt-in rank key (Metric::SqnNormalized); raw sqn and the default ranker
stay byte-unchanged, and the field carries #[serde(default)] (C18). Below the cap
(n ≤ 100) it equals raw sqn exactly. #135 (r-sma --trace):
r_sma_sweep_family persists each member's equity / exposure / r_equity under
runs/traces/<n>/<member_key>/ via the same persist_traces_r the single run uses;
per-member --trace is symmetric across all three sweep strategies.
[HISTORY — the built-in --strategy sweep triple (sma / momentum / r-sma) was
retired with the demos → blueprint-data (#159, cuts 1b-4). The per-member
--trace symmetry was NOT carried to the aura sweep <blueprint.json> path — it
was silently dropped at #159/#220 (run_blueprint_sweep never wired persist;
let _ = persist), and #168 makes the surface honest: sweep/walkforward (like
the pre-existing run/mc) now refuse --trace outright. See the CLI---trace
retirement amendment below.]
Realization (cycle 0068, #115 — position-event derive). [The derivation
survives as the deploy / reconciliation layer per the 2026-06-28 reframe — its
"realistic brokers consuming it" goal is retired; money is the live-edge concern.]
derive_position_events(record, instrument_id) -> Vec<PositionEvent> (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 Scalars, 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 stop-then-same-cycle reopen) emitting Close then the
opposite open at one event_ts (close first — the C8 ">1 event per instant" case
that forces a derived table, not a per-eval output). The close sizes the
actual book (the closed position's stored volume), never an exposure delta.
instrument_id is a caller-supplied scalar (aura-analysis depends only on
aura-core). 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). The r_col ⟷ PM-record lockstep is guard-pinned for
direction too.
Reframe (2026-06-28, #116 — realistic broker retired; cost-model graph in R; money to the live edge).
This is the live contract above. Ratified in an in-context design discussion
(reference issue #116). It preserves the durable spine — unsized bias stream
(sign = direction, magnitude = conviction), signal quality in R, the stop defining
R, and the decoupling of direction from sizing from fill — and the shipped Stage-1
realizations (the Bias node, FixedStop / vol_stop, PositionManagement,
summarize_r / RMetrics / sqn_normalized, SQN as ranking objective). The
RiskExecutor composite survives in shape (bias + price → stop-rule →
position-management) but with its Sizer interior removed and its risk_budget
argument dropped / vestigial (it sized the now-removed Sizer); the Veto remains an
optional documented seam. The contract supersedes the following, which were
design intent, largely unbuilt (#116 — the realistic broker and the whole
Stage-2 currency layer were never implemented; the Stage-1 R chain was) and are
retired:
- the "realistic broker" concept — an authored-friction historical broker —
rejected as "horseshoe-throwing" (real friction is not historically knowable),
replaced by the cost model: a composable C9 graph of cost nodes (in
aura-std/aura-composites), approximating not claiming realism, generalizing / subsuminground_trip_costintonet_expectancy_r; - the "Currency P&L is Stage 2" paragraph in full, the Stage-1-vs-Stage-2 hard sequencing gate, and currency / fixed-fractional / compounding in research — compounding is now a post-hoc money-management transform of the net-R sequence at the deploy / account layer;
- the
z⁻¹register on the fill edge and the flat-1R-vs-compounding structural axis as research mechanism — there is no equity → size edge in research, so no register and no such axis in the loop; - the Sizer in research and currency size /
volume— size is a deploy concept; the research executor is stop + position-management in R;PositionManagement'ssizeport is dropped or constant-driven (no dangling port, C8), itssizefield and the event table'svolumeare vestigial in research; - the "a broker is an ordinary in-graph node / no special external broker subsystem" forbid, for the live edge only: in-graph brokers are retired outright, and the live broker is now an explicit I/O adapter at the C11 recording / C13 deploy edge — not an in-graph node and not part of the research graph (the no-in-graph-broker-subsystem prohibition still holds inside the graph);
- the position-event table as the realistic-broker input and its
first-difference-of-the-book (
deal = target − book − in_flight) execution framing — the table (schema 0063 #114, derive 0068 #115) survives as the deploy / reconciliation artifact (real volume), not a research artifact and not "fed to a broker" in research. Money, a real broker, and cTrader Open API are a separate, later live / deploy-edge concern (C11 record-then-replay; C13 frozen-deploy invariant) — the onlybelastbareground truth, measured, never modelled. The honesty principle is explicit: the net-R curve is a research / ranking hypothesis; the forward / live run is ground truth.SimBrokeris downgraded to a legacy / optional pip yardstick, not to be expanded. (Terminology note: with Stage 2 gone, the "Stage-1" label below is no longer one half of a two-stage gate — it survives only as a historical cycle name, like theexposure→biason-disk alias, denoting the shipped feed-forward R chain; the identifier family that carried it was renamed to the r-family —r-sma/r-breakout/r-meanrev— in cycle 0100, #174.)
Realization (cycle 0081 — cost-model graph, cycle 1, #148). The first concrete
cost node + the net-R seam shipped. ConstantCost (aura-std) is an ordinary
downstream node (C9) emitting a 3-field cost-in-R record {cost_in_r, cum_cost_in_r, open_cost_in_r} isomorphic to PositionManagement's R-triple; R-pure
(cost_per_trade / |entry − stop|, notional cancels, no equity held). The scalar
round_trip_cost argument of summarize_r is retired: summarize_r folds a
co-temporal cost stream (positional 1:1 join — cost[i] is record[i]'s cycle) into
net_expectancy_r, one home for cost / no double-count, byte-identical to the old
cost = 0 baseline on an empty stream. The headline sink is net_r_equity =
LinComb(4)[cum_realized_r, unrealized_r, −cum_cost_in_r, −open_cost_in_r] → Recorder
(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).
[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
R-aggregation axis. Decision log: #148.
Realization (cycle 0082 — cost-graph composition, cycle 2, #148). The cost graph
composes: a second, state-dependent cost node plus an aggregator prove that two
cost nodes sum into one net-R curve with summarize_r and the net_r_equity tap
structurally unchanged. VolSlippageCost (aura-std) charges slip_vol_mult · vol / |entry − stop| in R, reading an independent short-horizon realized-range vol
(RollingMax − RollingMin, window distinct from the stop's own vol — scaling slippage
by the stop's vol would collapse cost-in-R to a constant, indistinguishable from
ConstantCost). CostSum (aura-std) is the cost-graph output node: it sums N
cost nodes' 3-field cost-in-R records per-field into one aggregate (n = 1 is the
identity), so the seam consumes a single cost stream regardless of node count — one
home for cost, the positional join unchanged. Co-temporality contract (load-bearing,
generalizes to all future factors): since summarize_r positionally joins cost[i] ↔ record[i], a cost node is gated only by the PM trade-geometry; any not-yet-warm
state input (the vol proxy warms later than PM) contributes 0 cost that cycle
rather than withholding — the node still emits its row, so the cost stream stays
co-temporal 1:1 with the PM record. This makes co-temporality structural and
warm-up-independent, preserves the C18 golden, and is honest (no slippage estimate yet
→ no charge). ConstantCost satisfies it trivially; only state-dependent nodes need
the missing-factor → 0 rule. Wired on the run path via --slip-vol-mult,
composable with --cost-per-trade (their costs sum); sweep / walk-forward / mc pass
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 generalCostNodetrait (now justified by two concrete nodes). Decision log: #148. [Superseded 2026-07-11 (#234): the--slip-vol-mult/--cost-per-tradeflags are gone; authoring moved to the campaigncostblock — 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
node abstraction: the 3-field cost triple is now one source of truth
(COST_FIELD_NAMES / COST_WIDTH, mirroring position_management::{FIELD_NAMES, WIDTH}), read by both producers + CostSum + the CLI wiring — the cycle-0082
by-convention lockstep is structurally gone (four restatements collapsed to one).
The CostNode factor trait carries a cost node's only per-node difference — the
price-unit cost numerator (cost_numerator(&mut self, &Ctx) -> f64), plus
extra_inputs (default none) and label; everything else is the generic
CostRunner<F: CostNode> adapter, which holds the shared cum/out state and
implements Node, writing the co-temporality skeleton (geometry-only gating,
numerator / latched R-normalization, the closed/open charge, the running cum, the
3-field emit) once. ConstantCost and VolSlippageCost are now thin factors whose
new() returns CostRunner<Self>. Honours C9 (a CostRunner<F> is a plain downstream
Node, no runtime sub-object) and C23 (name() dropped as dead surface — label()
remains the non-load-bearing symbol). Behaviour-preserving: the builders emit
unchanged schemas, so the wiring / net_r_equity seam / summarize_r are untouched and
the numerator / latched token form is byte-identical; the existing suite passes
verbatim and two new CLI characterization goldens pin the exact flat/composed
net_expectancy_r (the prior tests only asserted net < gross). Decision log: #148.
Realization (cycle 0084 — cost-graph composite-builder, cycle 4, #148). Decision E
ships. A new cost_graph(Vec<PrimitiveBuilder>) -> Composite in aura-composites (the
C16 layer that couples the engine builder + aura-std nodes) is the cost-model graph's
authoring primitive: it fans the 4 PM-geometry inputs to N cost nodes, surfaces
each node's extra inputs (discovered via schema().inputs[GEOMETRY_WIDTH..],
GEOMETRY_WIDTH now re-exported from aura-std) as cost[k].<port> composite roles,
sums them through CostSum, and exposes the 3-field aggregate. The CLI's manual
slot-indexed cost-wiring + the hardcoded MAX_RUN_COST_NODES = 2 cap are deleted — the
composite handles arbitrary arity. Behaviour-preserving (C11): the composite inlines
at bootstrap to the same flat fan-in, so the cycle-0083 net_expectancy_r goldens are
byte-identical (four aura-composites unit tests pin the exposed role-set + output
triple, incl. arbitrary-arity per-node namespacing). Honours C9 (ordinary downstream
nodes), C16 (wiring stays out of aura-engine), C23 (role/port names are
non-load-bearing). Carried debt (#152, for the deferred sweep-cost cycle): the
cost[k].<port> index-namespacing is restated across CostSum / cost_graph / the CLI
(a build-validated lockstep, not the silent positional kind 0083 collapsed), and
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"
clause is first realized. A cost factor now declares when it charges via
ChargeMode { AtClose, PerHeldCycle } (a defaulted CostNode::charge_mode(), default
AtClose), read by the one shared CostRunner — not a second runner type (a
commission is intrinsically at-close, a carry intrinsically per-held-cycle; the timing
belongs to the factor, preserving the cycle-0083 "skeleton written once" win). The
PerHeldCycle arm accrues per into a per-position acc every held cycle, dumps the
accrued total into cum at close (resetting acc), and marks the open position via a
growing open_cost_in_r. The first accrual node, CarryCost (aura-std,
a ConstantCost twin differing only in charge_mode()), is a labelled stress
parameter — the flat base of the accrual family; a run-path --carry-per-cycle flag
pushes it into the existing cost_graph/CostSum aggregation (no new wiring; a
per-trade and a per-held-cycle node compose, the costs summing per-field). Approach B
(honest bleed), achieved without a summarize_r fold change: the headline
net_r_equity curve bleeds continuously over the hold because the bleed lives in
open_cost_in_r, which the net_r_equity tap already subtracts — so summarize_r and
the CLI net_eq wiring are untouched, and the cycle-0083/0084 net_expectancy_r
goldens stay byte-identical (the AtClose arm is the pre-cycle-5 eval tokens verbatim).
The 3-field cost record's semantics generalize cleanly: cost_in_r = cost realized this
cycle (into cum); open_cost_in_r = the open position's cost marked-to-market
as-of-now (would-be-close for AtClose, accrued-so-far for PerHeldCycle) — singly
counted, no cum/open double-count (the two are mutually exclusive per cycle). Honours
C9 (a CostRunner<CarryCost> is an ordinary downstream Node), C11 (byte-identity), C16
(factor in aura-std, aura-engine domain-free), C23 (label() carries the rate). The
B-vs-A discriminator (a growing intra-hold open_cost_in_r, invisible to the scalar
net_expectancy_r) is pinned by unit B-proofs + a net_r_equity-bleeds-over-the-hold
integration test. Deferred (each its own #148 cycle): a notional-based carry
(price × rate, reads the price tap), the calendar-aware overnight swap proper
(rollover-boundary timing, 3× Wednesday, long/short asymmetry — deploy-edge realism), and
sweep-path cost. Decision log: #148.
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
(exit 2), not a silent no-op — refuse-don't-guess. Negative cost rates are
likewise rejected (exit 2) with a named diagnostic that identifies the offending
flag. CLI ergonomics only; the cost-model graph and the R math are untouched.
[HISTORY — the built-in --harness selector (its sma/macd non-R examples)
was retired with the demos → blueprint-data (#159, cuts 1b-4); the cost-flag
scoping rule survives on the aura <verb> <blueprint.json> r-sma run path over
examples/r_*.json.]
Realization (2026-07-06 — the risk regime as a structural campaign axis, #210).
The StopRule{Fixed,Vol} structural axis is realized at the campaign-document
level as CampaignDoc.risk: [RiskRegime] (a serializable, content-addressable
mirror of the runtime enum — aura-research, variants Vol{length,k} and
VolTf{period_minutes,length,k} (#262), the fixed-stop rule additive when
needed). It is a kept-separate matrix axis, a
peer of instruments and windows: the executor keys the nominee map by
(strategy, window, regime), so generalize aggregates across instruments
within a regime, never across regimes. Regimes are therefore compared at
presentation, never argmax-selected across — a cross-regime E[R] argmax would
compare R-multiples in different R units (the stop defines 1R), so the legacy
verbs' --stop-length/--stop-k joint stop grid is retired as a campaign
methodology (finding the best regime = the same walk-forward / worst-case-R
validation, run once per regime, and picking the most robust — a comparison, not
a search). Each member manifest stamps its resolved stop (default included),
closing the C18 gap; absent/empty risk = one implicit default regime,
absent-serializing for content-id parity. Two default representations
coexist by design (#217): a dissolved sweep binds no regime at all
(risk: []), late-resolved per member by stop_rule_for_regime at run
time, while walkforward/mc/generalize — whose --stop-length/--stop-k
became optional — bind the default regime eagerly into the campaign
document (risk: [Vol{length:3,k:2.0}]). Same R behaviour either way, but
deliberately different document content ids: a stop-less verb invocation's
document equals its explicit --stop-length 3 --stop-k 2.0 spelling, not
a stop-less sweep's document. Deferred (documented): regime-aware
trace persistence — the trace re-run and cell-key dir naming still assume the
default stop, since CellRealization carries no regime (#212); the core
run/stamp/generalize path is unaffected.
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<CostSpec> 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.