Files
Aura/docs/project-layout.md
T
claude 72c0b8ad21 fieldtest: cycle-241 wiring-only tier — 4 axes, 1 bug + 4 friction + 1 spec_gap + 5 working
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
2026-07-12 17:58:12 +02:00

196 lines
11 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 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 (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).