Anchor the world/playground bundle. C21 (the World: harnesses are dynamically constructible first-class objects; orchestrating families of them via the C12 axes is aura's differentiator, not the single backtest which is commodity; registry is the World's memory). C22 (the World is a program -> nothing displayable until it runs; the only durable displayable substance is the recorded trace; sinks (C8) are the recording mechanism into the registry (C18); the playground plays ANY harness and is a trace explorer / execution viewer, never a scene editor; observability is explicit/selective; samples ship with sinks). Sharpen C20 (open node vs closed harness; a harness is NOT a node, it is the closure that runs / the root scope; harnesses do not nest as nodes — the World orchestrates them as objects). Correct C14 (playground is core, not 'freely deferrable'). Foundation positioning (World = product, single-harness engine = substrate). CLAUDE.md invariant 12; project-layout day-in-the-life. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7.7 KiB
aura — project rules
aura is a "game engine for traders": a Rust framework and a playground to author trading nodes, backtest them deterministically and massively in parallel, compose them fractally, validate them (sweep / Monte-Carlo / walk-forward), and freeze a validated strategy into a standalone bot with a broker connection.
This file is the project sittenkodex. It imports the universal discipline from
~/dev/skills/templates/CLAUDE.md.fragment and adds aura's domain invariants.
The full architecture lives in the design ledger (docs/design/) and the specs
(docs/specs/), not here.
Roles
I am the orchestrator, not the implementer. The skills-plugin agents are my workers: I plan, design, decide, and integrate; they implement, refactor, test, and diagnose. Trivial mechanical edits I may do directly; anything needing broad reading or judgement goes to an agent. Agent reports describe intent, not outcome — I verify the diff and the test output myself before committing.
Commit discipline and main-branch sanctity
- Only the orchestrator commits. No skill agent runs
git commit; agents leave their output as unstaged working-tree changes for me to inspect and shape into commits. - main HEAD is sacrosanct. No
git reset/git reverton main. main moves forward only via my commits. Wrong agent output is discarded withgit checkout -- <paths>/git stash; a bad landing is fixed forward, never rewound. - When a commit closes a Gitea issue, reference it in the body:
closes #N(orrefs #Nfor non-final work).
Design rationale ≠ implementation effort
Design choices are justified by substance — semantics, structural fit, what the design permits vs forbids, compositional clarity, future-proofing. Implementation effort ("approach A touches 250 sites, B touches 1") is an observation about the current code, not a rationale. Effort is at most a named tiebreaker after substantive reasons tie.
Bug fixes — TDD, always
Bug fixes are RED-first and autonomous: the failing test exists in the working
tree before any fix. The debug skill is mandatory for any observable
misbehaviour (failing test, panic, wrong output).
Domain invariants (load-bearing — never silently violate)
These are the contracts the whole design rests on. A change that breaks one is a design decision, not a refactor, and belongs in the ledger.
- Determinism. A backtest is a deterministic, synchronous, non-concurrent event loop that reaches a unique state after each input tick. Same input → same run, reproducibly. Two backtests are fully disjoint → concurrently executable without locking. Parallelism is across sims, never within one.
- Causality / no look-ahead. A node sees only the past. Look-ahead is made structurally impossible (read-only input windows that end at the cursor; resamplers emit a bar only once it is complete), not merely discouraged.
- One merge, at ingestion only. Heterogeneous timestamped sources are k-way-merged into a single chronological cycle stream at the ingestion boundary. There is no merge / as-of join inside the graph.
- The four scalar base types, streamed as SoA. Only
i64,f64,bool,timestampare streamed, as columnar Structure-of-Arrays. Composite streams (e.g. OHLCV) are bundles of base columns. Non-scalars (String, Records, tables, calendars) exist as metadata beside the hot path, never in it. - Acyclic dataflow. The graph is a DAG; the only feedback path is an explicit delay/state node (the RTL "register"). The "cycle" of the research workflow is not a dataflow cycle.
- Record-then-replay determinism boundary. Anything non-deterministic,
external, or slow (LLM news agents, web sources) is materialized into a
recorded, timestamped stream before it enters the engine. The sim never
makes a live external call mid-replay. (See
~/.claude/CLAUDE.mdfor the IONOS consent rule: external LLM calls happen at the recording/live-source edge, with explicit per-session consent, never inside a backtest.) - Strategy result is a broker-independent position table. A strategy
outputs not an equity curve but a time-ordered table of position events
(
event_ts, action[buy/sell/close], position_id, instrument_id, volume; a position's open time is its opening event'sevent_ts); the set of open positions at t is its state. Brokers are downstream consumer nodes (not part of the strategy) that consume the position-event stream (plus prices) and emit an equity stream; several can be attached to the same position table at once, giving directly comparable curves. Neutral evaluation uses a deterministic, frictionless sim-optimal broker producing synthetic equity in pips; realistic broker nodes add real friction/constraints in currency for viability and deploy. - Deploy artifacts are frozen. Hot-reload (cdylib) is an authoring-loop tool only. The live bot is a statically-linked, versioned, frozen artifact — never hot-swapped (audit trail: this bot = this commit).
- Engine / project separation. This repo is the reusable engine. Research
projects are separate external repos that depend on it. Project-specific
signals live in a project's
nodes/; cross-project-reusable nodes in shared crates; universal blocks inaura-std(shipped here). Reuse is cargo-native; the hot-reload unit is always the project-sidecdylib. No user/project signals in this repo (onlyexamples/fixtures for the engine's own tests); no multi-project manager or node registry inside aura. A project is always a Rust crate (a cdylib library of node / strategy / experiment blueprints + a staticAura.toml), hosted byauraduring research and frozen to a standalone binary for deploy. - Authoring surface — all logic is Rust. Nodes, strategies, and
experiments/harnesses are authored in native Rust via Claude Code + the
skills pipeline, using builder APIs. Declarative config (
Aura.toml) carries only static project context, never logic — no experiment/strategy DSL (the RustAst trap). aura ships no embedded coding-LLM; IONOS LLMs are used only as a runtime data source (news bias), with per-session consent, never in the code path. - Construction is a bootstrap phase. Blueprints (param-generic graph-as- data from a Rust builder) are bootstrapped into frozen instances (buffers sized, topology fixed) by binding params + data + seed. Params configure and size nodes but never change topology (a topology change is a different blueprint). The harness — sources + strategy + broker node(s) + sinks — is the root sim graph, itself bootstrapped, and is C1's disjoint unit; its structural axes form the experiment matrix, its tuning params the sweep.
- The World is the product; the playground is a trace explorer. Three ontological tiers: a node is an open, composable fragment (at most one output, C8); a harness is the closed root graph that runs (sources + strategy + brokers + sinks + clock — C1's disjoint unit / root scope), not a node; the World is the program that dynamically constructs and orchestrates families of harnesses (walk-forward / sweep / MC / comparison) — aura's differentiator, not the single backtest. The World is a program: nothing is displayable until it runs, and what is displayable is exactly what a sink (C8) records into the registry (C18). The playground plays any harness and is an execution viewer / trace explorer (structure before, live streams during, recorded traces after) — never a scene editor; topology is grown in Rust + hot-reload, runtime params are UI-tunable.