Files
Aura/docs/project-layout.md
T
Brummel 4928e289f7 feat(project): the project-as-crate load boundary (cycle 0102)
A research project is now a loadable external cdylib crate. Inside a
directory whose ancestry holds an Aura.toml, aura discovers the project
root cargo-style, locates the compiled dylib via cargo metadata (debug
default, --release opt-in), loads it load-and-hold, and refuses
mismatches before trusting anything: the AURA_PROJECT descriptor
(aura-core::project, #[repr(C)]) carries a C-ABI stamp prefix (rustc +
aura-core version, baked per consuming build by the new aura-core
build.rs) validated before any Rust-ABI field is read. The vocabulary
charter gates the merged resolution: project type ids are ::-namespaced
(std stays bare), duplicates refuse, and the enumerable type-id list
must agree with the resolver, so introspection can never silently omit
a project type.

All blueprint verbs resolve through the merged project + std vocabulary
via a per-invocation Env threaded through the dispatch chains;
registry, trace-store, and data paths anchor at the project runs root
(Aura.toml [paths], paths-only by design — instrument geometry stays
the recorded sidecar, C15). RunManifest gains the Tier-1 project
provenance field (namespace + dylib sha256 + best-effort commit),
stamped beside topology_hash on the blueprint-run paths; pre-0102
registry lines load unchanged. Default node names strip the namespace,
so :: never reaches the param-path address space.

Proven by the demo-project fixture (built by the e2e via cargo,
path-dep on this workspace): run twice bit-identical, provenance
recorded, introspection lists demo::* beside std, registry anchors at
the discovered root from a subdirectory; the badcharter fixture proves
the charter refusal through the real libloading path; a never-built
project refuses with a cargo-build hint. Outside a project every path
collapses to the previous literals — goldens and manifest pins
byte-identical.

Verification: cargo build --workspace clean; cargo test --workspace 862
passed / 0 failed (incl. 7 project_load e2e); clippy -D warnings clean
(one precedent-matching allow(too_many_arguments) on run_oos_blueprint,
whose arity the Env threading raised to 8); doc build unchanged.
Docs/ledger aligned: Aura.toml field lists are paths-only in
project-layout.md, glossary, C16/C17; new C13 realization note records
the per-invocation-reload reading and the load-and-hold one-shot scope
boundary.

New deps, per-case review (aura-cli leaf binary only, never the frozen
artifact): libloading, toml.

refs #180
2026-07-02 18:13:37 +02:00

8.2 KiB
Raw Blame History

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 (the aura binary), and later the egui playground. It ships at most examples/ 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, its own Gitea repo (its own forward-queue), and a cargo dependency on aura. It is itself a Rust crate (a cdylib library of node / strategy / experiment blueprints); the aura host loads and runs it during research (hot-reload), and freezes a chosen strategy + broker into a standalone binary for deploy. This is where you and Claude author signals and experiments — all in Rust — and where the runs live. (Contracts C16, C20.)

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 always a Rust program built on the engine.

A project repo

ger40-lab/                 # your research project — a separate Rust crate (cdylib)
├── Aura.toml              # STATIC context only, paths only: data archive root,
│                          # runs dir (no logic; instrument geometry is the
│                          # recorded sidecar, C15 — never authored here)
├── Cargo.toml             # cdylib; depends on aura-core/aura-std (+ shared node crates);
│                          # carries an empty [workspace] table (see note below)
├── CLAUDE.md              # the project's own skills wiring (authoring discipline)
├── .claude/dev-cycle-profile.yml
├── nodes/                 # project-local node & strategy blueprints (Rust)
│   └── third-candle-long/
├── experiments/           # experiment/harness definitions (Rust): matrices, sweeps
├── runs/                  # the run registry: manifest + metrics per run
└── (frozen bots, …)

The empty [workspace] table. A project crate is its own cargo workspace, never a member of the engine's. This matters the moment a project 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 project's Cargo.toml. It pins the crate as a standalone workspace root and costs nothing when the project later sits in its own repo. The future aura new scaffolder will emit this line; until then, add it by hand.

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):

  1. aura-std — universal blocks shipped with the engine (SMA/ATR/RSI, resamplers, the SessionNode, standard combinators, broker profiles).
  2. Shared node crates — cross-project-reusable blocks in their own repos (e.g. Brummel/aura-nodes-fx), pulled in as cargo git dependencies.
  3. Project-local nodes/ — experimental, project-specific blocks.

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.

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.

A day in the life

  1. 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."
  2. Claude (via the skills pipeline) writes nodes/third-candle-long/, implements schema + eval against aura-core.
  3. Backtest: aura backtest nodes/third-candle-long --symbol GER40 --from 2020 --to 2024 → the strategy produces a broker-independent, unsized bias stream (one signed, bounded f64 ∈ [-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) under runs/. (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 legacy SimBroker pip yardstick survives only as an optional dual readout, not the model.
  4. Sweep / Monte-Carlo / matrix — a Rust experiment. Anything beyond a single backtest is an experiment in experiments/ (Rust, builder API): a parameter sweep, Monte-Carlo over seeds, or a structural matrix like "these 10 strategies × these 3 instruments × {fixed-stop, vol-stop} risk-executors". The matrix is plain Rust loops, not a config schema (C20). aura run experiments/compare bootstraps the matrix, fans the disjoint sims over all cores (C1), and writes the comparable runs to runs/.
  5. Compose: "combine it with momentum-filter as a weighted sum" → Claude writes a composite node (fractal, C9).
  6. Walk-forward: another experiment kind (rolling in-sample optimize + out-of-sample test) → an out-of-sample verdict.
  7. Freeze: aura freeze nodes/strategy-y --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.
  8. 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 (C20C22).

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).