Consumer-POV fieldtest evidence for the two-tier project model: the data-only quickstart, the role-2 attach loop, real-data usage inside a data-only project, and the refusal surfaces all match the docs. Inline fixes for two of the friction findings: - docs/project-layout.md: the day-in-the-life run example now shows real Unix-ms window bounds instead of bare years (--from/--to take ms). - `aura new` --help one-liner no longer calls the project a crate. Remaining findings tracked on the Gitea queue (empty first-manifest provenance on a fresh scaffold; misleading no-data message for an uncovered --real window; the tier hint missing on the graph-build path; the raw cargo-metadata leak for a missing [nodes] dir). refs #241
11 KiB
How aura is used — engine, projects, and a day in the life
This document describes the outside of aura: how you sit in front of it and
what a research session looks like. The inside (the engine's contracts) is the
design ledger (docs/design/INDEX.md).
Engine vs. project (the game-engine split)
aura is a game engine for traders, and like any game engine it is separate from the things built with it:
- This repo (
aura) is the engine:aura-core(the shared contract),aura-std(universal standard nodes),aura-engine(the deterministic sim runtime),aura-cli(theaurabinary), and later the egui playground. It ships at mostexamples/fixture nodes for its own tests — never your real signals. - A research project is its own external repo (e.g.
~/dev/ger40-lab/), with its own git history and its own Gitea repo (its own forward-queue), anchored by a staticAura.toml. Its default tier is data-only — blueprints, process, and campaign documents over the std vocabulary, no crate, no build step; theaurahost loads and runs it during research as is. The moment the project needs native node logic (role-2 work), it attaches a node crate (acdylib, scaffolded byaura nodes new) viaAura.toml [nodes]; theaurahost then hot-reloads that crate too, and freezing a chosen strategy + broker still yields one standalone binary spanning both halves for deploy. This is where you and Claude author signals and experiments, and where the runs live. (Contracts C16, C20, #241.)
The aura CLI is a tool you run inside a project directory — it finds the
project root by walking up to an Aura.toml, the way cargo finds Cargo.toml.
A project is a directory anchored by Aura.toml; native node logic never
lives in the project directory itself, only in an attached node crate (the
two tiers below).
A project repo (two tiers)
The default project is data-only — no crate, no build step. aura new
scaffolds exactly this:
ger40-lab/ # your research project — a plain directory, no crate
├── Aura.toml # STATIC context only: `[paths] runs` (the runs dir)
│ # and `[paths] data` (the archive root; defaults to
│ # the built-in Pepperstone path when unset); no
│ # logic — instrument geometry is the recorded
│ # sidecar (C15), never authored here. A `[nodes]`
│ # section, absent here, is what tier-selects a
│ # node crate in (see below).
├── CLAUDE.md # the project's own skills wiring (authoring discipline)
├── blueprints/ # op-scripts / campaign / process documents (data, no build)
└── runs/ # the run registry: manifest + metrics per run
Runs, sweeps, and campaigns over the std vocabulary work immediately —
nothing to compile. The moment a project needs a native node type (role-2
work), aura nodes new <name> scaffolds a sibling node crate and attaches
it via an Aura.toml [nodes] pointer:
ger40-lab/ # unchanged: still Aura.toml + blueprints/ + runs/
├── Aura.toml # now carries `[nodes] crates = ["../ger40-lab-nodes"]`
└── …
ger40-lab-nodes/ # a SIBLING directory — its own cdylib crate
├── Cargo.toml # cdylib; depends on aura-core (+ shared node crates);
│ # carries an empty [workspace] table (see note below)
├── CLAUDE.md # the node crate's own skills wiring
└── src/
└── lib.rs # the project's native node types, `<namespace>::`-prefixed
The aura host loads the pointed-at node crate during research (hot-reload);
freezing a chosen strategy + broker still yields one standalone binary for
deploy, spanning both halves.
The empty
[workspace]table (node-crate tier only). A node crate is its own cargo workspace, never a member of the engine's. This matters the moment a node crate lives under the engine repo (common while authoring, before it moves to its own repo): cargo otherwise walks up, finds the engine's root manifest, and refuses to build —error: current package believes it's in a workspace when it's not: ... to keep it out of the workspace, add an empty [workspace] table ...The fix is cargo's own hint: an empty
[workspace]table in the node crate'sCargo.toml. It pins the crate as a standalone workspace root and costs nothing when the node crate later sits in its own repo. Theaura nodes newscaffolder emits this line; a hand-rolled node crate adds it itself.
Where reusable nodes live (three tiers)
Everything that plugs into the engine is fractally a Node. Reuse is plain
cargo, layered by maturity (contract C16):
aura-std— universal blocks shipped with the engine (SMA/ATR/RSI, resamplers, theSessionNode, standard combinators, broker profiles).- Shared node crates — cross-project-reusable blocks in their own repos
(e.g.
Brummel/aura-nodes-fx), pulled in as cargo git dependencies. - The project's own node crate — experimental, project-specific blocks,
attached via
Aura.toml [nodes](scaffolded byaura nodes new; see the two tiers above).
Promotion is the ordinary Rust gradient: a node proves itself project-local →
gets extracted into a shared crate → if it turns universal, into aura-std.
Shared nodes are rlib dependencies; the hot-reload unit stays the project-side
cdylib that composes them, so editing a shared node still rebuilds and reloads
the dependent.
Every tier follows the same three-part pattern — a Node impl, a
PrimitiveBuilder recipe, and rostering the type id into a vocabulary
lookup — worked through end to end, with the scaffold's own starter node as
the example, in docs/authoring-guide.md, §0.
Authoring happens in Claude Code (contract C17)
aura has no built-in coding-LLM. You author by talking to Claude Code, which
writes the Rust — nodes, strategies, and experiments — builds it, runs it, and
reports back. Declarative config (Aura.toml) holds only static context, never
logic. IONOS LLMs appear only as a runtime data source (e.g. a news-agent node
emitting a bias), recorded before it enters a backtest, and only with your
per-session consent.
Wiring already-built node types into a graph, and naming experiment intent
over such a graph, is a separate, closed-vocabulary data surface, not Rust:
an op-script (aura graph build) assembles a strategy blueprint from node
types already in the vocabulary, a process document (aura process) names
a validation/eval methodology, and a campaign document (aura campaign)
names instruments × windows × strategy × param axes × process. All three are
headless-authorable and introspectable without a build. See
docs/authoring-guide.md for the worked grammar and
command sequences.
A day in the life
- You (in Claude Code, inside
ger40-lab/): "I suspect the 3rd 15m candle after GER40 open is long-biased when the first two close bullish — build it as a signal." - Claude (via the skills pipeline) attaches the project's node crate —
once,
aura nodes new ger40-lab-nodes— and implements theThirdCandleLongnode'sschema+evalin it, againstaura-core. - Backtest: an op-script wiring
ger40_lab_nodes::ThirdCandleLong(§1,docs/authoring-guide.md) is built intoblueprints/third-candle-long.json(aura graph build);aura run blueprints/third-candle-long.json --real GER40 --from 1704067200000 --to 1735689600000(the window bounds are Unix milliseconds — here 2024) → the strategy produces a broker-independent, unsized bias stream (one signed, boundedf64 ∈ [-1,+1]per cycle — sign = direction, magnitude = optional conviction). A downstream risk-based executor (stop-rule → position-management, in R, the protective stop defining 1R) turns the bias into tracked trades, and the R-evaluator integrates the per-trade R-outcomes into an R-expectancy / R-curve — the signal's quality, measured account- and instrument-agnostically in R (E[R], SQN), not currency. Optionally compose a cost model — a C9 graph of cost nodes, in R (net R = gross R − cost-in-R) — to draw the net-R curve beside the gross one. The run yields an R-metrics table + a run record (manifest + metrics) underruns/. (Contract C10.) Money, a real broker, and a currency curve are a later live/deploy-edge concern (the only reliable ground truth, measured forward — never an authored historical "realistic broker"); the legacySimBrokerpip yardstick survives only as an optional dual readout, not the model. - Sweep / Monte-Carlo / matrix — a campaign document. Anything beyond a
single backtest is experiment intent named in a campaign document
(data, not Rust, §3
docs/authoring-guide.md): a parameter sweep, Monte-Carlo over seeds, or a structural matrix like "these 10 strategies × these 3 instruments × {fixed-stop, vol-stop} risk-executors" — instruments × windows × strategy × param axes × process, headless-authorable and registered underblueprints/.aura campaign run <content-id>bootstraps the matrix, fans the disjoint sims over all cores (C1), and writes the comparable runs toruns/. - Compose: "combine it with
momentum-filteras a weighted sum" → Claude writes a composite node (fractal, C9). - Walk-forward: another experiment kind (rolling in-sample optimize + out-of-sample test) → an out-of-sample verdict.
- Freeze:
aura freeze blueprints/strategy-y.json --out bots/strategy-y→ a standalone, statically-linked bot (C13); the live broker connection (e.g. cTrader Open API) is bound at this deploy edge — the only place account money appears, measured forward against a real venue, never an authored historical broker. - Explore —
aura play. The playground plays any harness and shows what your sinks recorded (C22): live equity/signal streams while a run executes, and recorded traces + meta-views afterwards — stitched walk-forward equity, sweep surfaces, multi-strategy comparison. It is a trace explorer, not a scene editor (the World is a program; only sink-recorded data is visible — no sink, nothing to see). Topology you grow in Rust (hot-reload); runtime params you tune with sliders (C12).
The real value is not step 3 (one backtest — every quant system does that) but the World: steps 4/6 dynamically construct and orchestrate families of harnesses, and the playground explores those families. The single-harness engine is the substrate; the World is the product (C20–C22).
CLI command names and Aura.toml are illustrative; the concrete surface is
designed with the relevant milestone (see the open threads in the design ledger).
The forward-queue — what to try next — lives in the project's Gitea tracker, not
in code (contract C18).