72c0b8ad21
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
196 lines
11 KiB
Markdown
196 lines
11 KiB
Markdown
# 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 and its own Gitea repo (its own forward-queue),
|
||
anchored by a static `Aura.toml`. Its default tier is **data-only** —
|
||
blueprints, process, and campaign documents over the std vocabulary, no
|
||
crate, no build step; the `aura` host loads and runs it during research as
|
||
is. The moment the project needs native node logic (role-2 work), it
|
||
attaches a **node crate** (a `cdylib`, scaffolded by `aura nodes new`) via
|
||
`Aura.toml [nodes]`; the `aura` host 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's `Cargo.toml`. It pins the crate as a standalone workspace root and
|
||
> costs nothing when the node crate later sits in its own repo. The `aura
|
||
> nodes new` scaffolder 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):
|
||
|
||
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. **The project's own node crate** — experimental, project-specific blocks,
|
||
attached via `Aura.toml [nodes]` (scaffolded by `aura 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-guide.md#0-authoring-a-new-node-in-rust).
|
||
|
||
## 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`](authoring-guide.md) for the worked grammar and
|
||
command sequences.
|
||
|
||
## 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) attaches the project's node crate —
|
||
once, `aura nodes new ger40-lab-nodes` — and implements the
|
||
`ThirdCandleLong` node's `schema` + `eval` in it, against `aura-core`.
|
||
3. **Backtest:** an op-script wiring `ger40_lab_nodes::ThirdCandleLong` (§1,
|
||
`docs/authoring-guide.md`) is built into `blueprints/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, 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 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 under `blueprints/`. `aura campaign run <content-id>`
|
||
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 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.
|
||
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 (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).
|