Construction layer
Blueprint -> compilat: the construction phase that turns hand-written raw-index wiring into named graph-as-data, compiled to the flat instance the run loop runs.
Design settled (commit ff1dce5; ledger contract C23 + refinements to C9/C19): the bootstrap is a COMPILATION. A param-generic, named blueprint (the authoring source) is lowered to a flat, type-erased compilat wired by raw index; a COMPOSITE is a blueprint fragment compiled away by INLINING -- not a runtime sub-engine Node. Names are non-load-bearing debug symbols; the wiring resolves by index.
Scope of THIS milestone (the representation, not the optimiser):
- a Blueprint graph-as-data + a compile() lowering it to the (nodes, sources, edges) the existing Harness::bootstrap already consumes -- run loop UNCHANGED;
- a Composite as a nestable fragment that inlines at compile (index offset, edge rewrite, recursive); SMA-cross as the end-to-end demonstrator, bit-identical to the hand-wired graph (C1);
- aura graph rendering that blueprint as an ASCII DAG (#13 useful cut).
Issues: #12 (anchor -- blueprint repr + composite inlining), #13 (graph render).
Deferred to later milestones (named in C23 / the ledger open threads, NOT here):
- behaviour-preserving optimisation passes: intra-compilat CSE/DCE and sweep-invariant prefix hoisting (C23);
- the ergonomic named-handle wiring + experiment-builder API (structural axes, sweep combinators; C20). #21 (SimBroker slot-order footgun) belongs to this layer and is therefore NOT in this milestone.
C10 cost-model graph (in R): a composable C9 graph of cost nodes in aura-std producing net R = gross R - cost-in-R, plus a net_r_equity tap and folding into summarize_r. Per the 2026-06-28 C10 rework (#116, ledger 29cdc8c); supersedes the retired realistic-broker milestone (real friction is not historically knowable). Container for #148 and its cycle sub-issues. A bare gross-R run with no cost model stays valid.
One boundary question — where data authoring ends and Rust authoring begins — at three vocabulary sites, each needing its own mechanism. Domain invariant 10 wants native-Rust authoring as the escalation path for genuinely new logic, not the convenient path for composition or config; the 2026-07-22 external field test (triage: #314) showed the opposite pull: the agent wrote native nodes for four-node patterns fully expressible in the shipped vocabulary. #271 is the settled end of the boundary: non-scalar structural construction config (Session timezone/open-time) was ruled data-authorable at the user's request (2026-07-14, recorded in the issue); its typed construction-args mechanism remains to build. #310 (per-tap fold/Live subscription selection: ratify Rust-only, or add a data selector) and #317 (a named, registered composite subgraph referencable like a node) are the two still-open forks of the same boundary; deciding them together against the #271 precedent keeps the ledger ruling consistent instead of drifting per site.
Done: the boundary is recorded in the ledger for all three sites, and each site's mechanism (or ratified Rust-only status) conforms to it.
Why this milestone exists (motivation)
Aura can do a lot on paper — deterministic, massively-parallel backtests,
fractal composition, sweep / Monte-Carlo / walk-forward validation — but
none of it had ever been pointed at its actual purpose: finding a
genuinely profitable algorithmic strategy. The user has spent years
without a single live-profitable bot; every attempt failed live. The
working thesis behind Aura is that those failures were backtests that
lied — overfit, look-ahead leakage, cost-blind, one-regime flukes — and
that Aura's structural guarantees (look-ahead made impossible per C2,
determinism + walk-forward + Monte-Carlo) make it a lie-detector for
backtests: it cannot manufacture alpha, but it can give a truthful
verdict fast.
This milestone is the first real-world test of that thesis on two levels at
once: use Aura to search candidate strategies and either prove an edge or
honestly kill it, and in doing so test whether Aura itself is fit for
its purpose (a real-world shake-out of the engine).
Success is not a guaranteed profit. Success is a truthful per-candidate
verdict with look-ahead structurally impossible and out-of-sample
validation. A candidate that survives OOS + Monte-Carlo + costs across
several symbols is a real signal; most candidates will fail honestly, and
that is the apparatus working — not a setback.
Tradeable universe (low transaction cost only — user-set)
DAX = GER40, Nasdaq = NAS100, FX majors EURUSD, GBPUSD, USDJPY,
USDCHF, USDCAD, NZDUSD (AUDUSD is absent from the data server). All other
server symbols may be read as signal inputs but never traded. Data: M1
(ZIP-packed .bin), ~12 y for the indices (2014-08 → 2026-06), ~15 y for
the FX majors (2011-03 → 2026-06).
Methodology — the discipline that breaks the 5-year pattern
- Out-of-sample from the start — never tune on the test set.
- Walk-forward + Monte-Carlo as the default, not the exception.
- Few parameters, large data — every knob is an overfit dimension.
- Cross-symbol / cross-regime generalization is the killer test — a real
structural effect appears (directionally) across several instruments and
periods; an edge that pops in one symbol / one window / one param cell is
noise. (This already caught a GER40-only artifact — see the run log.) - Multiple-comparisons control — a sweep over K params × M symbols selects
the best-looking cell on noise; OOS + MC + cross-symbol consistency is the
control. - R first, currency/costs second — Stage-1 frictionless R signal quality
(ledger C10); Stage-2 (realistic broker, costs, equity feedback — the
existing broker milestone) is the gate a survivor then passes (E[R] > 0
before Stage 2).
Plan / phases
- Phase 0 — calibrate the null (DONE): the existing stage1-r SMA-cross toy
isE[R] ≈ 0/ slightly negative, frictionless, across the 4 wired symbols
— a clean honest null that proved the apparatus produces sane R-metrics on
real data end-to-end. See the run log. - Enabling tooling: CLI-griddable signal/stop timescales (DONE, shipped);
OOS validation harness (walk-forward + MC for R candidates, not just the
SMA sample — issue); register the remaining tradeable instruments
(issue). - Phase 1/2 — candidate search, OOS-validated from the start (live lead:
the index-trend hypothesis — issue). - Phase 3 — survivor → Stage 2 (costs / realistic broker, the existing
broker milestone) → eventual deploy / real-world test.
Current blocker (resume here)
A single multi-member sweep over full M1 history uses ~2 GiB per member
and nearly exhausts RAM + swap (measured: an ungated 9-member EURUSD sweep
peaked at 18.5 GiB RSS, drove swap to 30/31 GiB, dropped free RAM to 2.7 GiB,
and SIGKILL-137'd the original screen run). It must be fixed before the
walk-forward OOS phase, which multiplies sweep memory across windows. The
BLOCKER issue is the next item; the research resumes once it is fixed.
Make the shipped real-data session-breakout reachable by the World families (sweep / walk_forward / compare) by shipping it as a Composite blueprint with a clean param_space: structural deformers bound out (Delay.lag), the bar-period a single coupled knob feeding both Resample and Session. Absorbs #94 (form), #96 (period coupling + param_space hygiene), #97 (walk_forward non-degenerate). Spec follows a recon of the blueprint layer's param fan-out capability.
The World's massively parallel sweep (C12) is aura's differentiator, and it is also the most direct route to false discovery: selecting the best of a large family, picking sharp in-sample peaks, and confirming an edge on a single series all manufacture apparent edges out of noise. The hygiene invariants (C1 determinism, C2 no look-ahead) keep a single backtest honest; this milestone adds the inferential layer that keeps a selection across a family honest.
Walk-forward and the per-candidate out-of-sample bootstrap confidence interval are tracked in #139. This milestone covers the defenses #139 leaves unguarded: trials-deflation of the selected winner, plateau-over-peak selection, and cross-instrument generalization.
Three-layer plan making measurement a first-class citizen of the deterministic graph. Each layer moves semantics from ad-hoc code into declared structure:
- #280 — trustworthy time base. Bucket membership computes on normalized time, not raw provider stamps (today every consumer hand-rolls jitter rounding — or silently mis-buckets: the 08:59:59.4 bar counts toward the wrong candle).
- #281 — computation vocabulary. Four orthogonal standard cells (
Sign,Select,CumSum,When). The fabric already carries the hard semantics (freshness-gated recompute,Firing::Anyhold);Whenexposes it — after it, every existing reducer composes gated, unmodified. - #282 — declared observability. Taps as the output-side twin of
input_roles: the fragment declares measurement points, the harness/run mode binds recorders. An unbound tap is dead in the FlatGraph, so DCE removes the whole counter chain behind it — unobserved measurement evaporates at compile time; a reduce-mode sweep pays zero for study logic.
Trust is layered bottom-up: #281 graphs on unfixed candles compute precisely the wrong thing, and #282 taps would then publish it. Implementation may proceed independently; trust only stacks in order.
Deliberate boundary (tier ontology, not an oversight): order statistics (median) stay sink-side; multi-instrument inputs (e.g. a GER40 session gate on a UK100 study) stay harness/World tier.
Acceptance sketch: the origin question ("is the first UK100 5m candle after the DAX open a momentum indicator?") becomes expressible as a hand-authored blueprint (~50 nodes) plus one aura run --real UK100 with recording — zero lines of throwaway Rust.
Origin: UK100 open-momentum scratch study, 2026-07-17.
Prove the pure compute substrate carries arbitrary multi-producer / multi-consumer / multi-sink dataflow graphs deterministically and correctly, with no trading domain in scope.
In: sources and producing nodes (producers), transformer/join nodes (consumers), and real recording sink nodes; every fan-out / fan-in / diamond / deep / wide topology; the firing modes (A as-of, B barrier, mixed); all four scalar kinds and multi-field records; determinism (C1) under every combination, exercised by a stress matrix.
Out: position table, broker nodes, equity streams, and every other trading-domain model. Those move to a later milestone. This milestone closes when the infrastructure stress matrix runs green end-to-end.
Realizes the engine/project separation (domain invariant 9 / C16, docs/design/INDEX.md) as working machinery: a research project is an external Rust cdylib crate of node/strategy blueprints plus a static Aura.toml, scaffolded by aura new, discovered cargo-style by walking up to Aura.toml, loaded per invocation through the injected vocabulary-resolver seam blueprint loading already exposes (blueprint_from_json, crates/aura-engine/src/blueprint_serde.rs:165), and driven end-to-end by the existing blueprint verbs.
In scope: the cdylib load contract (load-and-hold lifetime, same-toolchain check), the project-resolver charter (project vocabulary plus std vocabulary, collision policy), the Aura.toml v1 schema, content-id canonicalization ahead of the first external consumer (#171), the aura new scaffolder, an authoring-loop end-to-end path, and a milestone fieldtest as a genuine external project crate.
Out of scope, deferred: experiments-in-crate (#147), freeze/deploy and the live broker edge (#149), and the hard-wired-harness retirement (#159).
Decision provenance, the recorded sequencing revision, and the full scope detail: #180.
Stage 1 of the C10 R-reframe (#117, ledger C10): a strategy's bias is turned into trades by a flat-1R RiskExecutor and scored in R-expectancy by an R-evaluator. Feed-forward, no equity feedback, no currency. Precedes and gates the Stage-2 realistic-broker milestone (entered only once E[R] > 0).
Goal: a genuine external Aura research project — own repo via aura new, own native nodes, real archive data, the full research loop (author, sweep, walk-forward, MC, generalize, campaign, reproduce, chart) — without hitting an unmapped wall. Scope is research-level only: freeze/deploy (C13, the operator role's live half, #149/#60) is explicitly out.
Role charter (C25 — the nine authoring roles, cut by artifact + surface + iteration cost): the project serves role 2 (extension dev: native project nodes), role 4 (data curator: archive + geometry sidecars via Aura.toml), role 5 (methodology designer: process documents), role 6a (strategy designer: blueprints/op-scripts), role 6b (campaign designer: campaign documents), role 7 (player: trace/chart exploration), and role 8 only in its research half (reproduce, manifests, audit of what ran). Roles 1/3 (system programmer, toolchain dev) are vendor-side; role 8's frozen-bot half is out of scope.
Basis (audited 2026-07-10, session evidence at file:line in the member issues): the strategy-fixture weld is gone from live code — no CLI verb special-cases a strategy or node type; the substrate (Composite.input_roles, k-way merge, open_ohlc) is already multi-role/multi-source capable. What remains are (a) the last fixture-era organ, the hard-wired wrap_r scaffolding (single "price" role bound to the Close column, synthetic pip on real data, no cost graph since #221, hardcoded stop in reproduce), (b) surface honesty/ergonomics walls a fresh project hits in week one (#228, #211, #226, #224, #230, #213), and (c) zero end-to-end evidence for the defining loop: a project-authored node with an open param through sweep/campaign.
Done means: the walls above are closed or consciously re-triaged, and a from-scratch external project can exercise every in-scope role surface on real data without leaving the public interface.
Deliver C10's deferred A-side so a strategy can be optimized net of real cost instead of by frictionless total_pips (which today rewards churn / selects overtraders). Today the only broker is the frictionless SimBroker (pips); there is no PositionEvent, no realistic broker, no currency/account/lot/contract-size concept anywhere.
Architecture decision (settled)
The realistic broker is a single-pass node on exposure + price (peer of SimBroker), tracking position state internally and charging friction on each exposure delta. The position-event table (event_ts, action[buy/sell/close], position_id, instrument_id, volume) is a decoupled post-run AUDIT artifact derived from the recorded exposure stream's first difference — not the broker's live input.
This resolves a real over-specification in C10 as written: "ordinary node" + "attaches on the derived table" + "table is computed post-run" cannot all hold without a two-pass run and a multi-column / multi-row-per-ts Source replay (which does not exist and carries a reversal determinism hole). We pick the single-pass reading and record the clarification in the ledger (see the ledger issue). Rejected: two-pass table-as-Source (over-machinery), post-run fold (gives up "broker is an ordinary node").
Settled defaults (overridable)
- Sizing (exposure fraction → lots): fixed-notional
|exposure|·account_size/contract_value, rounded tolot_step; one shared sizing policy used by both the broker and the table-derivation. - Frictions (first cut): constant per-instrument spread (reproducible from the manifest) + commission (per-lot/per-trade). Slippage declared no-op; margin/liquidation deferred.
- Account context: passed as broker constructor args (
account_size, currency label) — NOTAura.toml(that file is its own deferred cycle). - Single-instrument GER40 with an EUR account; quote-currency is a label only.
Stops / risk overlays
Stop-loss, take-profit, trailing-stop, time-stop are risk overlays in the decision layer, expressed in the exposure stream (C10: "risk lives in the decision/sizing node") — NOT broker features. Putting them in the broker would diverge the broker's position from exposure and break sim-optimal vs realistic comparability. The broker owns only stop-fill realism (slippage / gap-through), which is the slippage param of this milestone (no-op first cut). The protective-exit node family itself is separate strategy-authoring (aura-std), tracked as its own issue, not part of this milestone.
Deferred (named, NOT in this milestone)
Aura.toml + config loader (own cycle); FX conversion when quote ≠ account currency; slippage modeling; margin/liquidation account-state; per-bar executable spread (needs a 3rd broker input); multi-instrument. Whether the optimizer DEFAULT objective flips to net-currency is a separate, explicitly-ratified step (keep total_pips default, net-currency opt-in).
Definition of done
A single aura run --real <SYM> yields directly comparable sim-optimal (pips) and realistic (currency) equity curves plus the position-event audit table; optimize/rank_by can rank by net_currency_pnl; the C10 Architecture-A clarification is in the ledger; RunMetrics stays backward-compatible (#[serde(default)] on new fields).
The two closed-vocabulary data artifact types from the #188 role-model design pass: process documents (role 5, validation methodology) and campaign documents (role 6b, experiment intent), with parse/validate/introspection/content-addressing first and execution semantics in later cycles. Design basis: #188 body + addenda + fork-triage + resolution comments.
Cheap, high-corroboration hardening of the real-data ingestion seam before the next headline milestone (realistic-broker C7/C10 vs compare+visual-face C21/C22). Groups: #80 Timestamp-window M1FieldSource::open, #81 open_default_archive + re-export DataServer/DEFAULT_DATA_PATH, #92 canonical 4-source OHLC opener (C4 merge order), #93 ts-join for multi-tap recorder traces, #22 minimal per-instrument pip metadata Source (NOT Aura.toml), #73 flat-run-registry fork, #82 two-store Registry::open rustdoc. Close before re-deciding A/B.
The 2026-07-22 external field test (triage: #314) showed an agent holding only the release binary reverse-engineering document schemas from CAS artifacts under runs/ because no surface explains itself. #316 records the ratified principle (user decision, 2026-07-23: self-description becomes a ledger design principle — every surface explains itself) and carries the two forks that settle first, so no surface is documented twice: how far mandatory doc-strings extend across the vocabulary surfaces, and how strict a register-boundary shape gate may be. #315 is the CLI subset carved out of the same evidence: a help-text concepts layer explaining the research verbs as sugar over process/campaign documents, plus meaning-text for the metrics/nodes/taps/folds rosters that print blank from the binary today. #267 contributes the onboarding-card content the field evidence makes necessary either way (bias-as-target-position + protective-stop semantics; which verbs persist traces); whether it stays a separate generated card is subject to the post-#316 re-examination recorded as a scope note on #267 (2026-07-23) — self-explaining surfaces may make a generated card partially or wholly redundant.
Done: the principle is in the design ledger, and the #315/#267 surfaces conform to its settled doc-string scope.
aura-std gains the stateful/temporal/logic/resampling/session nodes missing for non-trivial strategies, driven by the GER40 15m session-breakout. Phase 1 (engine-side): 7 nodes + a synthetic e2e fixture. Spec: docs/specs/0050. The real GER40 consumer crate is Phase 2 (separate repo, C9).
All file references are at engine commit 592750b. Design-ledger contract codes (C-numbers) are glossed inline; their full texts live in docs/design/INDEX.md.
The responsibility model
The codebase divides into eight responsibilities: six ladder layers ordered by domain specificity (inner to outer), one process column standing beside the ladder, and the shell.
The ladder, inner to outer:
- engine — the domain-free deterministic streaming runtime and vocabulary: cells/kinds/freshness/firing, graph building, compilation, execution, taps/trace (the determinism/causality/composition contracts C1–C9, construction C19, compilation C23, blueprint-as-data C24); the domain-free node roster (24 arithmetic/logic/rolling nodes plus the 3 generic sink nodes
recorder/gated_recorder/series_reducer, out of the 41 node files incrates/aura-std/src/); and generic statistics (the Monte-Carlo mechanicsmonte_carlo/resample_block/MetricStatsincrates/aura-engine/src/mc.rs, the multiple-comparison hurdle math currently inaura-analysis). As a layer name, engine is wider than the crateaura-engine: the layer spansaura-core,aura-engine, the domain-free part ofaura-std, and the generic statistics. - market — what a market is, carrying no analysis or strategy intent: instruments and pip/point geometry (contract C15), session anchoring (
crates/aura-std/src/session.rs), bar resampling (resample.rs), market-data ingestion (aura-ingest). - measurement — descriptive statistics over market streams: session statistics, conditional rates, distributions. Sibling of strategy: both consume market, neither imports the other.
- strategy — the definition of trading signals: bias, stop rules, sizing, cost models (
bias.rs,stop_rule.rs,sizer.rs,longonly.rs, the five cost nodes inaura-std; the composite graphs inaura-composites). - backtest — the evaluation of strategies: simulated execution without money (
sim_broker.rs,position_management.rs), the R-metric reductions and the position-event table (aura-analysis), and the run scaffold currently welded into the CLI (wrap_r,crates/aura-cli/src/main.rs:1489-1667). Backtest mirrors execution on the research side — execution semantics in R units, money exiled. - execution — money, sizing in account terms, the live broker. Exists today only as a ratified boundary (C10/C13: account money lives only at the live deploy edge).
The process column — the research-process machinery: run records and the append-only registry (contract C18, aura-registry), the process/campaign document types (aura-research), campaign execution (aura-campaign). It consumes run artifacts, not market streams, and orchestrates runs of any ladder layer; it stands beside the ladder, not on it. Under this assignment the aura-registry → aura-research production dependency is column-internal and legal.
The shell — the CLI verbs and dispatch (aura-cli). Imports everything; imported by nothing.
Bridge to the earlier numbering: #286 (the shape-dispatch proposal — a blueprint declaring only taps runs bare, without the backtest scaffold) states the same model as five numbered layers. The mapping: layer 1 keeps the name engine, 2 trading = market, 3a = measurement, 3b splits into strategy + backtest (definition vs evaluation — the split is new), 4 = execution; the process column is new — it has no place on a five-rung ladder, which is why aura-registry/aura-research/aura-campaign resisted layer assignment.
Import rules
- Ladder: an outer layer may import inner layers, never the reverse. measurement and strategy are siblings (no import either way); backtest may import strategy.
- Column: imports the engine layer plus the run-artifact and metric interfaces; no ladder crate imports the column; column-internal edges are free.
- Shell: imports everything; nothing imports the shell.
dev-dependenciesare exempt — tests may cross layers. The existingengine→std,ingest→std,registry→stdedges are dev-only and stay.- The rules bind crate structure, not graph composition: a blueprint may feed a measurement statistic into a bias input — data flow inside a graph is free. Layers govern imports; graphs compose freely.
Verified state at HEAD
- The dependency direction already nearly obeys the rules. The only hard production violation is
aura-engine→aura-analysis(the engine layer importing backtest reductions; declared incrates/aura-engine/Cargo.toml, re-exported via the engine'sreport::), plus R-typed halves inside the engine's ownmc.rs(bias_sign_flipsatcrates/aura-engine/src/mc.rs:41,RBootstrap/r_bootstrapatmc.rs:181-213, sharing a file with the generic mechanics). - The interweaving is inside crates, not between them.
aura-stdholds four layers in one roster: engine (27 of 41 node files), market (session.rs,resample.rs), strategy (bias/stops/sizer/costs) and backtest (sim_broker.rs,position_management.rs).aura-analysisholds backtest reductions and generic hurdle math in one crate — its own header: "R-based signal-quality metrics, the broker-independent position-event table, and the multiple-comparison hurdle math". - measurement has no run verb:
dispatch_runrefuses any blueprint without abiasoutput (crates/aura-cli/src/main.rs:3589-3603) and welds the backtest scaffold onto every run. Measured cost for a measurement-shaped run: 2013 MB peak RSS over 12 years, linear in cycles; full evidence in #286.
Target crate structure
| Crate | Layer | Content |
|---|---|---|
aura-core, aura-engine |
engine | runtime unchanged; the crate aura-engine loses the report:: re-exports and the R-typed halves of mc.rs |
aura-std |
engine | reduced to the domain-free roster |
aura-stats (new) |
engine | MC mechanics from mc.rs, hurdle math from aura-analysis |
aura-market (new) |
market | session/resample from aura-std, C15 instrument geometry |
aura-ingest |
market | unchanged |
aura-measure (new) |
measurement | trace reducers; gains its run verb via the shape dispatch (#286) |
aura-strategy (new) |
strategy | bias/stops/sizer/costs from aura-std, plus aura-composites |
aura-backtest (new) |
backtest | sim_broker/position_management from aura-std, the wrap_r scaffold as a library, R metrics from aura-analysis |
aura-research, aura-registry, aura-campaign |
process column | unchanged |
aura-cli |
shell | thinned: shape dispatch instead of the inline scaffold |
Crate names are provisional. Enforcement is the crate graph itself: once crates are cut along these cells, a layer violation is a compile error and every new edge a visible Cargo.toml diff — no linter, no discipline appeals.
Interfaces
Five of the six seams are already ratified contracts; only the metric interface is new:
| Seam | What crosses it | Anchor |
|---|---|---|
| engine ↔ any vocabulary | the node contract + kinds | C8 |
| topology ↔ engine | blueprint as data; the blueprint's shape (exposes bias vs declares only taps) selects the scaffold |
C24, #286 |
| run ↔ analysis | declared taps / trace — the only way values leave a run | C27 |
| run ↔ process column | the run record, append-only | C18 |
| process column ↔ metrics | a named metric vocabulary, supplied by measurement and backtest instead of baked in R-only | new |
| everything ↔ money | the deploy edge | C10/C13 |
Phases
Each independently shippable; behaviour byte-identical except the purely additive shape dispatch:
- Ratify a ledger contract naming the layers, the column, and the import rules.
- Cut the one hard edge
aura-engine→aura-analysis: splitmc.rs(generic mechanics stay engine-side, R-typed halves move backtest-side), dissolve thereport::re-exports. - Shape dispatch plus the
wrap_rscaffold as a library — the seed ofaura-backtest; gives measurement its run verb and removes the measured 2 GB retention (#286). - Split the
aura-stdroster along engine / market / strategy+backtest — pure moves with re-exports. - Split
aura-analysisinto generic statistics and backtest metrics. - Generify the column's metric interface — demand-driven, once measurement campaigns need it (the hurdle math then applies verbatim to, e.g., a statistic swept across instruments under multiple-comparison discipline).
Open detail questions (where exactly the cost models land, whether Latch is domain-free, re-export deprecation windows) belong to the phases, not to this description.
Two ways the release binary reports in a form that misleads a reader or a CI grep. #278 (observed in the campaign-scaling wake, #251; per-invocation counts raised further by the parallel cell loop, #277): benign gate-emptied notices and genuine per-cell faults share one undifferentiated aura: cell ... stderr idiom, so a real fault drowns among expected null-result notices. #313 (2026-07-22 external field test, triage: #314): a walk-forward whose 184 of 184 out-of-sample windows recorded zero trades completed indistinguishably from honest success, and downstream conclusions were drawn from an all-zero metrics table. The order is forced by #313's own deferral: #278 settles the stable, machine-separable marker scheme for the two stderr classes; #313's campaign-end all-zero-trades notice then slots into that scheme — the reverse order would decide the stderr format twice.
Done: the two stderr classes are visually and grep-separably distinct, and a walk-forward that never traded announces itself.
First cut of the World/orchestration layer (C21), scoped deliberately to the sweep root — the smallest gate that unlocks all four C12 orchestration axes.
The walking-skeleton substrate runs one harness; the construction layer compiles a blueprint to a flat compilat. This milestone makes a blueprint yield a family of disjoint instances driven by an injected parameter vector, and runs the simplest axis (param-sweep) over it.
Scope (the root, in order)
- Cycle A — param declarations (C8). A node declares its tunable parameters: typed, with ranges, aggregating into the blueprint's param-space. Today params are hard-coded in the builder (SMA(2), Exposure(0.5)); C23 names this gap explicitly ("deliberately not in the schema yet").
- Cycle B — param-set injection into bootstrap (C19/C12). The bootstrap binds (blueprint + param-set + data + seed); make the param-set an injected vector, not baked into the builder. One blueprint, many instances, no cdylib recompile.
- Cycle C — param-sweep (C12.1). A family of disjoint compilats from one blueprint, run in parallel (C1), each yielding metrics. The first real "N instead of 1" moment.
- Cycle D — run registry over the sweep family (C18). Index/compare manifests + metrics across the family (per-run manifest+metrics already exist since cycle 0009).
Deferred (later milestones)
- Optimize (C12.2 — argmax over the sweep), Walk-forward (C12.3), Monte-Carlo (C12.4 — sweep over seeds).
- Playground / trace explorer (C22) — strictly after the World; its meta-views (sweep surfaces, WF stitching) need sweep/WF results to show.
- Project layer (
aura new+ experiment-builder API + Aura.toml, C16/C20) — orthogonal packaging; the sweep mechanics can be developed in the engine's own examples/ first.
Ledger refs: C8, C12, C18, C19, C21, C23.
The three remaining C12 orchestration axes (optimize/argmax, walk-forward, Monte-Carlo) plus the family infrastructure, layered over the proven disjoint-parallel sweep core (aura-engine/src/sweep.rs). Completes C12/C21 — aura's differentiating meta-level: orchestrating families of harnesses, not the single backtest.
Axis 1 (grid sweep) shipped in the prior "The World — parameter-space & sweep" milestone. This milestone cashes in the substrate that was purpose-built for exactly this and nothing else: the atomic sim unit (bootstrap_with_params), the disjoint-parallel executor (sweep_with_threads), the run registry, and the (manifest, metrics) pair. Each new family is a thin layer over that core — high value, low structural risk.
Load-bearing design constraint (eager-agnostic dataflow): NO issue in this milestone may settle on the assumption that input streams are materialized Vecs. At realistic scale (20y, 3 tick streams, ~7.9e9 ticks) the eager layout is ~190 GB resident — non-residable. The firewall is #71: realize the deferred Source trait (ledger 0004) as the run() ingestion seam, co-designed with the real DataServer-backed source (closes the cycle-0011 deliberate-eager gap; C12 Arc<[T]> cross-sim sharing). Every downstream family inherits the producer Source shape — see the per-issue eager-agnostic constraint comments on #66/#68/#69/#70/#52.
Constituent issues (dependency order):
- #71 — Source-trait ingestion seam + DataServer-backed source (FOUNDATIONAL, lands first; everything below inherits the producer shape)
- #66 — Seed-as-input plumbing (seeded
Source; prerequisite for Monte-Carlo) - #57 — Named sweep point in the per-point closure (readable per-point params)
- #52 — Random param-sweep enumeration (C12.1 other half; seeded-RNG warm-up)
- #67 — Optimize/argmax axis (C12 axis 2) — SHIPPED
- #68 — Monte-Carlo family (C12 axis 4)
- #69 — Walk-forward orchestrator (C12 axis 3) — the PRIMARY eager-agnostic firewall: WindowBounds + make_source(from,to), never a materialized-history slice
- #70 — Registry lineage for families + CLI (C18; lands last)
Out of scope (later milestones): the egui playground meta-views (C22) and the C23 sweep-invariant hoisting optimisation — both presuppose these families exist. Bayesian/genetic param-search policies (pluggable atop the atomic unit) are also deferred.