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

142 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).