docs: two-tier project model — layout, guide, glossary, ledger, invariant 9 (#241 T7)

- CLAUDE.md invariant 9 amended end to end: a project is a directory
  anchored by a static Aura.toml, data-only by default; native node
  logic lives in attached node crates (the nodes/ antecedent removed —
  user decision 2026-07-12, re-opened by the role model's diagnosis).
- docs/project-layout.md reworked to the two tiers (data-only default
  tree + sibling node crate, workspace note under the crate half); the
  day-in-the-life walkthrough now attaches the project's node crate
  once and wires the namespace the shown command actually produces.
- docs/authoring-guide.md: data-only quickstart (closed run target,
  open sweep target) + `aura nodes new` as the native entry; section
  cross-reference updated.
- docs/glossary.md: new "data-only project" and "node crate" entries;
  Aura.toml and manifest entries carry the tier split.
- docs/design/INDEX.md: realization note (wiring-only tier, #241) in
  the project-environment section.
- rustdoc: `[nodes]` intra-doc-link escapes in project.rs/main.rs.

Full gates green: workspace suite (all binaries), clippy -D warnings,
doc build without new warnings.

closes #241
This commit is contained in:
2026-07-12 17:45:59 +02:00
parent 23bb978bf2
commit 0170ec277f
7 changed files with 128 additions and 65 deletions
+79 -45
View File
@@ -15,53 +15,80 @@ from the things built with 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**.)
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 always a Rust program built on the engine.
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
## 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 separate Rust crate (cdylib)
├── Aura.toml # STATIC context only, paths 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
├── Cargo.toml # cdylib; depends on aura-core/aura-std (+ shared node crates);
│ # carries an empty [workspace] table (see note below)
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)
├── .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, …)
├── blueprints/ # op-scripts / campaign / process documents (data, no build)
── runs/ # the run registry: manifest + metrics per run
```
> **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 —
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 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 `aura new`
> scaffolder emits this line (cycle 0103); a hand-rolled project adds it
> 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)
@@ -73,7 +100,9 @@ cargo, layered by maturity (contract C16):
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.
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`.
@@ -110,10 +139,13 @@ command sequences.
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
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 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
@@ -126,18 +158,20 @@ command sequences.
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/`.
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 nodes/strategy-y --out bots/strategy-y` → a standalone,
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.