docs(readme, guides, glossary, ledger), bench: the surviving surface everywhere
Slice 8, closing the #319 retirement's prose and bench debt. Bench argv rides exec (fixed_cost single-run probe, campaign surfaces, and the seed helper that still called the retired sweep at runtime — caught by the bench run itself); all five fingerprints unchanged against the committed baselines, confirming record-line parity through the retirement. Live docs (README, authoring guide, project layout, glossary) describe only exec + campaign documents + graph introspect --params; three glossary entries explicitly mark their verb retired. Ledger amendments per the C29 discipline — C25's executor-verb-set re-settled (exec + the --override residue), C14's dual grammar reduced to the one dispatch, C24's document-built runs/families + gated-intake route list (lockstep with the code comment), C12's override clause and identity anchor repointed to the runner-layer hash computation, C18/C22/C27/C01 mention rewrites — superseded sentences moved verbatim to the history sidecars (c25's created). Stale dual-grammar doc comment on is_blueprint_file rewritten. refs #319
This commit is contained in:
@@ -31,47 +31,46 @@ Invoke it as `aura <command> …` (examples below use the plain name).
|
||||
|
||||
- **Blueprint** — a serialized signal graph as data: a param-generic
|
||||
`price → bias` node graph. A blueprint file (`blueprint.json`) is the unit a
|
||||
downstream consumer loads and drives verbs over.
|
||||
downstream consumer loads and executes or varies.
|
||||
- **Open vs. closed** — a blueprint with unbound (free) numeric knobs is *open*;
|
||||
one with every knob bound is *closed*. Different verbs want different states
|
||||
(see each verb below).
|
||||
- **Family** — the set of runs one verb produces over a blueprint (a sweep grid,
|
||||
a Monte-Carlo seed set, a walk-forward window sequence). Families are persisted
|
||||
in a content-addressed store and can be listed, ranked, and reproduced.
|
||||
one with every knob bound is *closed*. A plain `exec` wants closed; naming a
|
||||
knob as a campaign axis (or an `exec --override`) is what reopens it.
|
||||
- **Family** — the set of runs a campaign document's process pipeline produces
|
||||
over a blueprint (a sweep grid, a Monte-Carlo seed set, a walk-forward window
|
||||
sequence). Families are persisted in a content-addressed store and can be
|
||||
listed, ranked, and reproduced.
|
||||
- **Axis** — one named, sweepable knob of a blueprint (e.g. `fast.length`),
|
||||
bound with a comma-separated value list. A **gang** fuses several sibling
|
||||
knobs into one axis (one value drives all members).
|
||||
|
||||
## Running & orchestrating a loaded blueprint
|
||||
## Executing a document
|
||||
|
||||
These verbs all take the **blueprint file as their first positional argument**.
|
||||
Most drive a family of runs over it; `graph` renders its structure so a mis-wire
|
||||
is visible before a run. `walkforward`, `mc`, and `generalize` share
|
||||
`sweep`'s generic grammar — the blueprint positional plus `--real` and a
|
||||
repeatable `--axis <name>=<csv>` (`generalize` takes `--real <SYM1,SYM2,…>`, at
|
||||
least two instruments, with a single value per axis) — see `--help`.
|
||||
`aura exec <target>` (#319) is the one executor verb: `<target>` is either a
|
||||
loaded **blueprint** file (a single synthetic run) or a **campaign document**
|
||||
(a `.json` file or its registered 64-hex content id — instruments × windows ×
|
||||
strategy × param axes × process, see `docs/authoring-guide.md` §3). `graph`
|
||||
renders a blueprint's structure so a mis-wire is visible before a run.
|
||||
|
||||
| Command | Purpose |
|
||||
|---|---|
|
||||
| `aura run <bp.json>` | Run one backtest of a **closed** blueprint and print its report. An open (free-knob) blueprint is refused with a clean error — bind it, or use `sweep`. |
|
||||
| `aura exec <bp.json>` | Run one backtest of a **closed** blueprint and print its report. An open (free-knob) blueprint is refused with a clean error — bind it, or vary it as a campaign axis. |
|
||||
| `aura exec <bp.json> --override <node.param>=<value>` | Reopen one **bound** param for this single execution only (the value is recorded raw in the manifest); repeatable. |
|
||||
| `aura exec <bp.json> --tap <name>=<fold>` | Subscribe a declared tap to a fold (`record`/`count`/`sum`/`mean`/`min`/`max`/`first`/`last`) for this run; repeatable. |
|
||||
| `aura graph <bp.json>` | **Render** the blueprint's structure as an interactive HTML DAG so a mis-wire is visible before a run. Omit the file to render the built-in sample; a named-but-unreadable file is a usage error. |
|
||||
| `aura sweep <bp.json> --list-axes` | **Discover** the blueprint's open, sweepable knobs. Prints each as `<name>:<kind>`. Run this first to learn the axis names. |
|
||||
| `aura sweep <bp.json> --axis <name>=<csv> [--axis …]` | Run a **grid family** over the named axes and persist it. |
|
||||
| `aura mc <bp.json> --seeds <n>` | Run a **synthetic Monte-Carlo family** of `n` seeded realizations. Wants a **closed** blueprint (the inverse of sweep). |
|
||||
| `aura mc <bp.json> --real <sym> --axis <name>=<csv> [--axis …]` | Run a **Monte-Carlo R-bootstrap campaign** over recorded data — driven through the same generated-campaign pipeline as `sweep`; `--block-len`/`--resamples`/`--seed` tune the bootstrap. |
|
||||
| `aura walkforward <bp.json> --axis <name>=<csv> [--axis …]` | Run an **in-sample-refit walk-forward family** — rolling optimize + out-of-sample test across windows, stitched into one verdict + parameter stability. `--select` chooses the per-window objective. |
|
||||
| `aura graph introspect --params <bp.json\|id>` | **Discover** a blueprint's open, sweepable knobs. Prints each as `<name>:<kind>`, bound params trailing `default=<value>`. Run this first to learn the axis names for a campaign document. |
|
||||
| `aura exec <campaign.json\|id> [--parallel-instruments <n>]` | Execute a **campaign document** — a grid family, gates, walk-forward, Monte-Carlo, and cross-instrument generalization per its process document, one cell per (strategy, instrument, window). |
|
||||
| `aura runs families` | List every persisted family (id, kind, member count). |
|
||||
| `aura runs family <id> [rank <metric>]` | List one family's members, optionally ranked best-first by an R metric (e.g. `sqn_normalized`, `expectancy_r`). |
|
||||
| `aura reproduce <family-id>` | Re-derive every member of a persisted family from the content-addressed store and check it is bit-identical (the C18/C1 determinism guarantee). |
|
||||
|
||||
**Important contract — every open knob is mandatory.** On `sweep` and
|
||||
`walkforward`, the knobs enumerated by `--list-axes` are *all* required: you must
|
||||
supply an `--axis` for each open knob. There is no default value — pin a knob you
|
||||
don't want to vary with a single-value axis (`--axis name=4`). Omitting one is a
|
||||
clean error naming the missing knob, not a silent default.
|
||||
**Important contract — every open knob is mandatory on a campaign axis.** A
|
||||
campaign document's `strategies[].axes` must name every open knob
|
||||
`graph introspect --params` lists: there is no default value — pin a knob you
|
||||
don't want to vary with a single-value axis (`{"kind":"I64","values":[4]}`).
|
||||
Omitting one is a clean error naming the missing knob, not a silent default.
|
||||
|
||||
Use `aura <cmd> --help` for the full data-window (`--real`/`--from`/`--to`),
|
||||
naming (`--name`), and selection (`--select`) flags.
|
||||
Use `aura exec --help` for the exact flag grammar, `docs/authoring-guide.md`
|
||||
for the campaign document shape (data window, naming, presentation/emit).
|
||||
|
||||
## Authoring & introspecting topology
|
||||
|
||||
@@ -111,11 +110,11 @@ Each element is one op, tagged by `"op"`. Node params are bound with the **typed
|
||||
```
|
||||
|
||||
Piping this document into `aura graph build` emits a `blueprint.json` you can
|
||||
then feed to `run` / `sweep` / `mc` / `walkforward` above:
|
||||
then feed to `exec` above:
|
||||
|
||||
```sh
|
||||
aura graph build < crossover.ops.json > crossover.bp.json
|
||||
aura sweep crossover.bp.json --list-axes
|
||||
aura graph introspect --params crossover.bp.json
|
||||
```
|
||||
|
||||
The op kinds are `source`, `input`, `add`, `feed`, `connect`, `expose`, `tap`,
|
||||
|
||||
@@ -95,31 +95,18 @@ fn blueprint_stems(dir: &Path) -> Vec<String> {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Seed one blueprint into the scratch store via a tiny synthetic sweep (the
|
||||
/// E2E `seed_blueprint` pattern) and return its content id.
|
||||
/// Seed one blueprint into the scratch store via `graph register` (#319: the
|
||||
/// surviving registration surface) and return its content id.
|
||||
fn seed_blueprint(bin: &Path, dir: &Path, file: &str, name: &str) -> Result<String, String> {
|
||||
let before = blueprint_stems(dir);
|
||||
let (out, code) = run_in(
|
||||
bin,
|
||||
dir,
|
||||
&[
|
||||
"sweep",
|
||||
file,
|
||||
"--axis",
|
||||
"fast.length=2,4",
|
||||
"--axis",
|
||||
"slow.length=8,16",
|
||||
"--name",
|
||||
name,
|
||||
],
|
||||
)?;
|
||||
let (out, code) = run_in(bin, dir, &["graph", "register", file, "--name", name])?;
|
||||
if code != Some(0) {
|
||||
return Err(format!("seed sweep failed ({code:?}): {out}"));
|
||||
return Err(format!("seed register failed ({code:?}): {out}"));
|
||||
}
|
||||
blueprint_stems(dir)
|
||||
.into_iter()
|
||||
.find(|s| !before.contains(s))
|
||||
.ok_or_else(|| "seed sweep must store a new blueprint".to_string())
|
||||
.ok_or_else(|| "seed register must store a new blueprint".to_string())
|
||||
}
|
||||
|
||||
fn register_process(bin: &Path, dir: &Path, file: &str, doc: &str) -> Result<String, String> {
|
||||
@@ -261,7 +248,7 @@ pub fn winner_fingerprint(stdout: &str) -> Result<String, String> {
|
||||
|
||||
fn campaign_rep(bin: &Path, sizing: Sizing, process_doc: &str) -> Result<RepOutcome, String> {
|
||||
let (scratch, doc) = build_scratch(bin, sizing, process_doc)?;
|
||||
let timed: TimedChild = run_timed(bin, &["campaign", "run", &doc], &scratch.0)?;
|
||||
let timed: TimedChild = run_timed(bin, &["exec", &doc], &scratch.0)?;
|
||||
if timed.exit != Some(0) {
|
||||
return Err(format!(
|
||||
"campaign run exited {:?}\nstdout: {}\nstderr: {}",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! CLI fixed cost: the spawn floor (`aura --help`, no fingerprint — nothing is
|
||||
//! computed) and a minimal data-only project run whose fingerprint is the
|
||||
//! FNV-1a hash of the run's single stdout JSON line. Fresh scratch project per
|
||||
//! computed) and a minimal data-only project exec whose fingerprint is the
|
||||
//! FNV-1a hash of the exec's single stdout JSON line. Fresh scratch project per
|
||||
//! repetition, so store counters start identical.
|
||||
|
||||
use super::{median, RepOutcome};
|
||||
@@ -11,7 +11,7 @@ use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
use std::time::Instant;
|
||||
|
||||
/// Fingerprint of an `aura run` record line, invariant across rebuilds: the
|
||||
/// Fingerprint of an `aura exec` record line, invariant across rebuilds: the
|
||||
/// line's `manifest.commit` is the aura binary's compile-time build sha
|
||||
/// (crates/aura-cli/build.rs — the invariant-8 audit trail), so hashing the
|
||||
/// raw line would flip the fingerprint on EVERY new commit and report a
|
||||
@@ -59,7 +59,7 @@ pub fn rep(bin: &Path) -> Result<RepOutcome, String> {
|
||||
let mut run_walls = Vec::new();
|
||||
let mut first_line = None;
|
||||
for _ in 0..RUN_SAMPLES {
|
||||
let timed = run_timed(bin, &["run", "bench_sma_a.json"], &scratch.0)?;
|
||||
let timed = run_timed(bin, &["exec", "bench_sma_a.json"], &scratch.0)?;
|
||||
if timed.exit != Some(0) {
|
||||
return Err(format!("aura run exited {:?}: {}", timed.exit, timed.stdout));
|
||||
}
|
||||
|
||||
@@ -953,9 +953,9 @@ struct ExecCmd {
|
||||
tap: Vec<String>,
|
||||
}
|
||||
|
||||
/// The dual-grammar discriminator: a first-positional that names an existing
|
||||
/// `.json` file selects the loaded-blueprint branch. Single-sourced so the
|
||||
/// four dual-grammar subcommands stay in lockstep.
|
||||
/// The file-target discriminator: a first-positional that names an existing
|
||||
/// `.json` file is a document on disk. Shared by `exec`'s target routing
|
||||
/// (layered with `is_campaign_document_file`) and `dispatch_graph`.
|
||||
fn is_blueprint_file(arg: &Option<String>) -> Option<&str> {
|
||||
arg.as_deref()
|
||||
.filter(|a| a.ends_with(".json") && std::path::Path::new(a).is_file())
|
||||
|
||||
@@ -247,9 +247,9 @@ std vocabulary, anchored by `Aura.toml`. There is no crate and no build step.
|
||||
unit R, and quality metrics are R-based. Entry signals become held state
|
||||
via the signal-side latch/edge-pulse idiom (see
|
||||
`aura graph introspect --vocabulary`).
|
||||
- Data plane: the research verbs are sugar over registered process/campaign
|
||||
documents — author them directly with `aura process` / `aura campaign`,
|
||||
growing one from a bare `{}` via `aura campaign introspect --unwired`.
|
||||
- Data plane: process/campaign documents are authored directly with
|
||||
`aura process` / `aura campaign`, growing one from a bare `{}` via
|
||||
`aura campaign introspect --unwired`.
|
||||
"#;
|
||||
|
||||
/// Render a data-only project template: only `__NAME__`/`__NAME_SNAKE__`
|
||||
|
||||
+45
-37
@@ -10,7 +10,7 @@ JSON artifact kinds you author headlessly along that arc:
|
||||
through `aura process validate|introspect|register|show`;
|
||||
3. a **campaign document** (role 6b) — experiment intent (instruments ×
|
||||
windows × strategy × axes × process) through `aura campaign
|
||||
validate|introspect|register|show|run|runs`.
|
||||
validate|introspect|register|runs|show`, executed by `aura exec` (#319).
|
||||
|
||||
Each section below is a worked, verified example — every command shown was
|
||||
run against this repo and the output is transcribed, not invented. The *why*
|
||||
@@ -39,9 +39,11 @@ the same shape every node in `aura-std` already follows.
|
||||
scaffolds a **data-only** project (`docs/project-layout.md`, "A project
|
||||
repo (two tiers)") — a strategy over the std vocabulary is a
|
||||
blueprint/campaign document, not Rust: the scaffold ships **one** closed
|
||||
`signal.json` starter that serves both verbs — `aura run` uses its bound
|
||||
values as-is, `aura sweep --axis fast.length=2,4,8` overrides them
|
||||
(bound = default, not fixed); `--list-axes` lists the open knobs and the
|
||||
`signal.json` starter that serves both legs of `exec` (#319) — `aura exec
|
||||
signal.json` uses its bound values as-is, `aura exec signal.json --override
|
||||
fast.length=8` reopens one for that run, and a campaign document's
|
||||
`strategies[].axes` reopens one across a family (bound = default, not
|
||||
fixed); `aura graph introspect --params` lists the open knobs and the
|
||||
bound defaults alike, so every override target is discoverable.
|
||||
- **A project-specific *native* node type** — the moment §0's three-part
|
||||
pattern is actually needed — lives in an attached **node crate**:
|
||||
@@ -240,7 +242,7 @@ are dotted `<identifier>.<port>` on both sides of a wire.
|
||||
| `feed` | `{"op":"feed","role":<str>,"into":[<port>, …]}` | fan a previously-declared role into one or more interior input slots, all-or-nothing (a failing target leaves none of the batch wired). |
|
||||
| `connect` | `{"op":"connect","from":<port>,"to":<port>}` | wire one interior output field to one interior input slot. A `connect` that would close a dataflow cycle is rejected immediately — the only legal feedback path is an explicit delay/state node (domain invariant 5). |
|
||||
| `expose` | `{"op":"expose","from":<port>,"as":<str>}` | promote an interior output field to a boundary output under the alias `as` — a real *alias* (a terminal boundary name, not a referenceable identifier like `add`'s `name`). Together with `tap`, one of the two ops whose `as` key is a terminal name rather than an identifier. |
|
||||
| `tap` | `{"op":"tap","from":<port>,"as":<str>}` | declare a **measurement tap** on an interior output field under the name `as` — the output-side twin of `expose` (a recorded observation point, not a boundary output; a `Composite.taps` entry, C27). A single `aura run` constructs a recorder at each declared tap and persists its per-cycle series as a `ColumnarTrace`; a sweep leaves it inert. Tap names are their own namespace and must be unique (a second `tap` under one name refuses: `duplicate tap name`). |
|
||||
| `tap` | `{"op":"tap","from":<port>,"as":<str>}` | declare a **measurement tap** on an interior output field under the name `as` — the output-side twin of `expose` (a recorded observation point, not a boundary output; a `Composite.taps` entry, C27). A single `aura exec` constructs a recorder at each declared tap and persists its per-cycle series as a `ColumnarTrace`; a campaign member run leaves it inert. Tap names are their own namespace and must be unique (a second `tap` under one name refuses: `duplicate tap name`). |
|
||||
| `gang` | `{"op":"gang","as":"channel_length","into":["channel_hi.length","channel_lo.length"]}` | Fuse two or more sibling params into ONE public knob: the member addresses leave the sweepable param space and `as` replaces them; the bound or swept value fans out to every member at bootstrap. Members must share one scalar kind and stay open (un-bound). |
|
||||
|
||||
Omitting `name` is fine for a single strategy, but it has three live
|
||||
@@ -283,9 +285,10 @@ corpus's own example, verified below; byte-identical to the on-disk
|
||||
|
||||
A `bind` in an `add` op pins that param to a value and removes it from the
|
||||
**open** param space (`--params`): a bound param is a **default** (#246) — a
|
||||
run uses it as-is, while any campaign axis or `aura sweep --axis` naming it
|
||||
re-opens it for that family and binds it per cell. `--list-axes` lists it
|
||||
after the open knobs as `<name>:<KIND> default=<value>`. `bind` is for a
|
||||
plain `exec` uses it as-is, while a campaign axis naming it re-opens it for
|
||||
that family (bound per cell) and `exec`'s own `--override NODE.PARAM=VALUE`
|
||||
(#319) re-opens it for that one execution. `graph introspect --params` lists
|
||||
it after the open knobs as `<name>:<KIND> default=<value>`. `bind` is for a
|
||||
value the strategy carries by default; leave a param unbound, as all three
|
||||
are here, to make binding it mandatory for every sweep.
|
||||
|
||||
@@ -293,7 +296,7 @@ A param therefore has three states: **open** (an axis every sweep MUST bind),
|
||||
**bound** (a default any axis MAY override, #246), and **ganged** (open, but
|
||||
fused with its siblings under ONE public knob declared by a `gang` op; the
|
||||
member addresses are unbindable and only the gang's own single-segment name —
|
||||
e.g. `channel_length` — appears in `--list-axes`).
|
||||
e.g. `channel_length` — appears in `graph introspect --params`).
|
||||
|
||||
### Worked example: declaring a measurement tap
|
||||
|
||||
@@ -322,10 +325,12 @@ bias scaling) under the name `spread`:
|
||||
|
||||
The built blueprint carries a `taps` array naming the resolved wire
|
||||
(`{"name":"spread","from":{"node":<sub's index>,"field":0}}` — the name is
|
||||
addressed, the index is resolved *for* you). A single `aura run <blueprint>`
|
||||
addressed, the index is resolved *for* you). A single `aura exec <blueprint>`
|
||||
then constructs a recorder at that point and persists the `spread` series as a
|
||||
`ColumnarTrace` in the run's trace store, chartable by its name. A tap is inert
|
||||
in a sweep (no per-cell recorder) — it is a single-run observation surface.
|
||||
in a campaign member run (no per-cell recorder) — it is a single-run
|
||||
observation surface (or, per campaign, what a nominee's `persist_taps`
|
||||
re-run records, §3).
|
||||
(`sub.value` is tapped here purely to illustrate; any interior output field is
|
||||
a legal tap source, and a producer needs no other consumer to be tapped.)
|
||||
|
||||
@@ -422,29 +427,32 @@ slow.length:I64
|
||||
bias.scale:F64
|
||||
```
|
||||
|
||||
These printed names are the **one** axis namespace (#328): op-script params, a
|
||||
campaign document's `strategies[].axes` keys (§3), and `aura sweep <blueprint>
|
||||
--list-axes` / `--axis` (glossary `sweep`) all speak the same raw
|
||||
`<node>.<param>` form — `--params` and `--list-axes` are line-identical (open
|
||||
params bare, bound params trailing `default=<value>`), and every discovered
|
||||
name is verbatim legal as a document axis key, bound params included (#246's
|
||||
re-open contract):
|
||||
These printed names are the **one** axis namespace (#328): op-script params
|
||||
and a campaign document's `strategies[].axes` keys (§3) speak the same raw
|
||||
`<node>.<param>` form as `graph introspect --params` prints (open params
|
||||
bare, bound params trailing `default=<value>`) — every discovered name is
|
||||
verbatim legal as a document axis key, bound params included (#246's
|
||||
re-open contract), and as the token `exec --override NODE.PARAM=VALUE` (#319)
|
||||
takes on the single-run leg:
|
||||
|
||||
```json
|
||||
"strategies": [ { "ref": { "content_id": "…" },
|
||||
"axes": { "fast.length": { "kind": "I64", "values": [3, 5, 7, 9] },
|
||||
"bias.scale": { "kind": "F64", "values": [0.25, 0.5] } } } ]
|
||||
```
|
||||
|
||||
A single execution reopens the same raw name directly, with no document at all:
|
||||
|
||||
```
|
||||
$ aura sweep smacross.json --axis fast.length=3:9:2 # synthetic: raw works
|
||||
$ aura sweep smacross.json --axis bias.scale=0.25,0.5 --real ... # real: raw works
|
||||
$ aura exec smacross.json --override fast.length=5 # single run: raw works
|
||||
```
|
||||
|
||||
The older `<blueprint>.<node>.<param>` wrapped form (e.g. `graph.fast.length`,
|
||||
what pre-#328 transcripts and `--list-axes` output used to print) is retired
|
||||
from the surface; naming it refuses with a translation pointer to the raw
|
||||
candidate, on both intake seams:
|
||||
what pre-#328 transcripts and the retired `--list-axes` output used to print)
|
||||
is retired from the surface; naming it in a campaign document refuses with a
|
||||
translation pointer to the raw candidate, the one remaining intake seam:
|
||||
|
||||
```
|
||||
$ aura sweep smacross.json --axis graph.fast.length=...
|
||||
aura: axis "graph.fast.length": axis names are raw node.param paths —
|
||||
use "fast.length" (the wrapped --list-axes form was retired, #328)
|
||||
|
||||
$ aura campaign validate wrapped.json # a document quoting an old transcript
|
||||
aura: campaign references do not resolve:
|
||||
strategy f5fdf729d0333f0286c19d57143fb7933137f27a6128e72489c508096c7a2240:
|
||||
@@ -704,7 +712,7 @@ campaign document valid (executable): pipeline shape and static guards pass
|
||||
```
|
||||
$ aura campaign register mra_3_campaign_full_v2.json
|
||||
registered campaign 42edebd2159de708009ba21e1ed4aea2cffabc373bf761c5765f79c190b677bd (…/runs/campaigns/42ed…json)
|
||||
$ aura campaign run 42edebd2159de708009ba21e1ed4aea2cffabc373bf761c5765f79c190b677bd
|
||||
$ aura exec 42edebd2159de708009ba21e1ed4aea2cffabc373bf761c5765f79c190b677bd
|
||||
{"family_id":"42edebd2-0-GER40-w0-s0-0","report":{...}}
|
||||
{"family_id":"42edebd2-0-GER40-w0-s0-0-r1","report":{...}}
|
||||
…
|
||||
@@ -730,19 +738,19 @@ regime's family ids and trace dirs carry the `-r1` ordinal suffix (the
|
||||
default/first regime stays unsuffixed), each cell record names its regime,
|
||||
and generalization is keyed per regime — regimes are compared, never pooled.
|
||||
|
||||
`aura campaign run` is register-then-run sugar for a `.json` file, but the
|
||||
`aura exec` (#319) is register-then-run sugar for a `.json` file, but the
|
||||
canonical address is always the content id — running a bare file the first
|
||||
time registers it implicitly. `aura campaign runs` lists stored
|
||||
realizations; `aura campaign runs <id>` dumps the bare stored record(s) (not
|
||||
the `{"campaign_run": …}` emit wrapper above). If `presentation.persist_taps`
|
||||
is non-empty, the run also persists the named taps under
|
||||
`runs/traces/<trace_name>/…`, chartable with `aura chart`. The
|
||||
`sweep`/`walkforward --trace` analog persists every member's taps as a
|
||||
family charted the same way, by the handle the run prints (members keyed
|
||||
`<cell>/<member>`) — also by the `--trace <NAME>` you chose, when that name
|
||||
uniquely names one recorded run (`aura chart <NAME>` resolves it against the
|
||||
stored campaign documents; a name reused across runs refuses rather than
|
||||
guessing which one you mean).
|
||||
is non-empty, the run also persists every member's taps under
|
||||
`runs/traces/<trace_name>/…` — the deterministic `{campaign8}-{run}` handle
|
||||
the `campaign_run` record's `trace_name` field prints, never a user-chosen
|
||||
name — chartable with `aura chart <trace_name>` (members keyed
|
||||
`<cell>/<member>`); `aura chart` also resolves a bare campaign id/name
|
||||
against the stored campaign documents when no literal trace directory
|
||||
matches, refusing rather than guessing when the name is ambiguous across
|
||||
recorded runs.
|
||||
|
||||
Exit codes: a clean run exits 0; a usage error exits 2; a refusal before any
|
||||
cell runs (invalid document, missing project, unresolvable strategy) exits 1;
|
||||
|
||||
@@ -23,3 +23,7 @@ never line-ordered. Duplicate campaign instruments are refused at both the
|
||||
validate tier and the executor's preflight: the per-cell family name embeds
|
||||
the raw instrument string, so uniqueness is what keeps concurrent appends from
|
||||
racing one name's run-index assignment.
|
||||
|
||||
**Current-state cross-command-path example (superseded by the #319 sugar
|
||||
retirement, 2026-07-25):** "e.g. a swept member's `sqn` vs the same cell
|
||||
re-run under `aura generalize`".
|
||||
|
||||
@@ -5,8 +5,9 @@ loop that reaches a unique state after each input tick. Same input (incl. seed)
|
||||
→ bit-identical run. Two backtests are fully disjoint and run concurrently
|
||||
without locking. The bit-identity is *per run*: one backtest of given (inputs,
|
||||
seed) reproduces byte-for-byte. It does **not** extend to a *derived metric*
|
||||
recomputed for the same params by two *different command paths* (e.g. a swept
|
||||
member's `sqn` vs the same cell re-run under `aura generalize`), which may differ
|
||||
recomputed for the same params by two *different command paths* (e.g. a
|
||||
campaign cell's `sqn` within its sweep stage vs the same params re-run
|
||||
through `exec`'s blueprint leg with `--override`, #319), which may differ
|
||||
by floating-point reassociation (≤1 ULP) because the two paths accumulate the
|
||||
same logical reduction in a different operation order (IEEE-754 non-associativity).
|
||||
C1 governs the determinism of a single run, not the cross-command bit-identity of
|
||||
|
||||
@@ -34,3 +34,15 @@ walk-forward families (axes 2–4 above) build their members over **one** shared
|
||||
`Arc<DataServer>` (one `FileCache`), so a window is parsed once and every member's
|
||||
`M1FieldSource` borrows the same cached `Arc<[M1Parsed]>` chunks
|
||||
(`crates/aura-ingest/src/lib.rs:316`). The single-pass *parse* cost stays tracked as #95.
|
||||
|
||||
**Current-state bound-override + identity-anchor clause (superseded by the
|
||||
#319 sugar retirement, 2026-07-25):** "so axis 1 (param-sweep) may name a
|
||||
bound param — the family boundary re-opens it on the probe and on every
|
||||
member reload, and the axis binds it per cell; `run`/`mc` still require
|
||||
every param resolved (a truly open param refuses). Identity is untouched:
|
||||
`content_id_of` (`crates/aura-research/src/lib.rs`) and `topology_hash`
|
||||
(`crates/aura-cli/src/main.rs`) read the authored document, never a
|
||||
re-opened probe, and each member manifest records its per-cell bindings
|
||||
(#246, ratified 2026-07-12; the restriction this amends — axes bind only
|
||||
open knobs — was an implementation consequence of `bind()` shrinking the
|
||||
param surface, not a recorded decision)."
|
||||
|
||||
@@ -5,12 +5,17 @@ RNG-seed) → deterministic run → metrics`. Parameters are typed, ranged, runt
|
||||
values injected at graph build — no recompile per param-set; the optimizer sees a
|
||||
generic vector of typed ranges. A **bound blueprint param is that param's
|
||||
default**: "open" means *must be bound by an axis*, "bound" means *default,
|
||||
overridable by an axis*, so axis 1 (param-sweep) may name a bound param — the
|
||||
family boundary re-opens it on the probe and on every member reload, and the axis
|
||||
binds it per cell; `run`/`mc` still require every param resolved (a truly open
|
||||
param refuses). Identity is untouched: `content_id_of`
|
||||
(`crates/aura-research/src/lib.rs`) and `topology_hash`
|
||||
(`crates/aura-cli/src/main.rs`) read the authored document, never a re-opened
|
||||
overridable by an axis*, so a campaign axis (param-sweep) may name a bound
|
||||
param — the family boundary re-opens it on the probe and on every member
|
||||
reload, and the axis binds it per cell; `exec`'s blueprint leg still requires
|
||||
every param resolved (a truly open param refuses), and its own `--override
|
||||
NODE.PARAM=VALUE` (#246 residue, #319) reopens exactly the named bound param
|
||||
for that one execution — the single-member analogue of a campaign axis.
|
||||
Identity is untouched: `content_id_of` (`crates/aura-research/src/lib.rs`) is
|
||||
the one hashing primitive, read directly off the authored document by
|
||||
`aura_runner::member`'s `run_signal_r`/`run_blueprint_member` — where the
|
||||
manifest's `topology_hash` is computed inline (#319 Task 9 retired the
|
||||
CLI-side wrapper that used to duplicate this one call) — never a re-opened
|
||||
probe, and each member manifest records its per-cell bindings (#246, ratified
|
||||
2026-07-12; the restriction this amends — axes bind only open knobs — was an
|
||||
implementation consequence of `bind()` shrinking the param surface, not a recorded
|
||||
|
||||
@@ -27,3 +27,12 @@ every hand-rolled usage line reads `Usage: aura <verb> …` (#179, cycle 0101);
|
||||
refusal diagnostics stay unprefixed (diagnostics are not usage lines). The
|
||||
machine-first help surface (JSON/manifest help, stdin op-scripts) stays on the
|
||||
#157/C21 track, not this cycle (settled as human/GNU convention compliance).
|
||||
|
||||
**Current-state "Dual grammar" section (superseded by the #319 sugar
|
||||
retirement, 2026-07-25):** "The four dual-grammar subcommands
|
||||
(run/sweep/walkforward/mc) keep both grammars under one token via an
|
||||
optional `[blueprint]` positional plus a post-parse `is_file()` dispatch; the
|
||||
execution layer is unchanged (arg-plumbing via thin `*_from` adapters). The
|
||||
machine-first help surface (JSON/manifest help, stdin op-scripts) is
|
||||
deferred to the #157 / C21 track, distinct from this human/GNU-convention
|
||||
compliance."
|
||||
|
||||
@@ -59,12 +59,16 @@ caller branches on the failure class without parsing stderr.
|
||||
in `crates/aura-cli/src/main.rs`, threaded from the run registry —
|
||||
[C18](c18-registry.md)).
|
||||
|
||||
**Dual grammar.** The four dual-grammar subcommands (run/sweep/walkforward/mc)
|
||||
keep both grammars under one token via an optional `[blueprint]` positional plus
|
||||
a post-parse `is_file()` dispatch; the execution layer is unchanged (arg-plumbing
|
||||
via thin `*_from` adapters). The machine-first help surface (JSON/manifest help,
|
||||
stdin op-scripts) is deferred to the #157 / [C21](c21-world.md) track, distinct
|
||||
from this human/GNU-convention compliance.
|
||||
**Single document grammar (#319, 2026-07-25).** The five dual-grammar
|
||||
subcommands (run/sweep/walkforward/mc/generalize) that used to keep two
|
||||
grammars under one token are retired; one verb, `exec <target>`, dispatches
|
||||
on `is_blueprint_file` (a first-positional naming an existing `.json` file
|
||||
selects the loaded-blueprint leg; anything else — a campaign file or a
|
||||
registered content id — the campaign leg). The execution layer this
|
||||
collapses onto is unchanged in kind (arg-plumbing via thin adapters). The
|
||||
machine-first help surface (JSON/manifest help, stdin op-scripts) is
|
||||
deferred to the #157 / [C21](c21-world.md) track, distinct from this
|
||||
human/GNU-convention compliance.
|
||||
|
||||
**Two artifact classes, two redundancy budgets** (#249, ratified 2026-07-13).
|
||||
The data layer the programmatic face emits is a public interface read raw (by
|
||||
|
||||
@@ -271,3 +271,27 @@ and both callers are untouched; maintenance is lazy-only — put-time indexing
|
||||
was rejected because it would need a roster-free doc-level identity function
|
||||
whose equivalence to the loaded-composite path no green test ratifies (decision
|
||||
log: #191 comments).
|
||||
|
||||
**Current-state `runs.jsonl`/`families.jsonl` CLI clauses (superseded by the
|
||||
#319 sugar retirement, 2026-07-25):** "No live producer writes it today —
|
||||
sweep / walk-forward / mc persist to the family store and `aura run` does not
|
||||
persist — so the flat lib API is retained but selectively live: `rank_by`
|
||||
backs `aura runs family … rank`, `optimize` backs walk-forward's in-sample
|
||||
step (`aura-campaign` and the registry's own selection helper both call it),
|
||||
while `append`/`load` remain public API with no in-tree caller (a latent
|
||||
surface for external consumers)." … "CLI: `aura runs families`, `aura runs
|
||||
family <id> [rank <metric>]`; `aura sweep`/`walkforward`/`mc` persist via
|
||||
`append_family` with an optional `--name`."
|
||||
|
||||
**Current-state "Cross-instrument generalization" paragraph (superseded by
|
||||
the #319 sugar retirement, 2026-07-25):** "`FamilyKind::CrossInstrument`:
|
||||
`aura generalize` runs one candidate across an instrument list and persists
|
||||
the M per-instrument runs via `append_family`, each member self-identifying
|
||||
through `RunManifest.instrument`. The generalization score (worst-case R
|
||||
floor + sign-agreement + per-instrument breakdown) is a **recomputable
|
||||
aggregate** over those members, not a persisted family-level record."
|
||||
|
||||
**Current-state "The campaign executor" opening sentence (superseded by the
|
||||
#319 sugar retirement, 2026-07-25):** "`aura campaign run <file|content-id>`
|
||||
executes a campaign (a file is register-then-run sugar; the content id is
|
||||
canonical): a zero-fault referential gate, then the process pipeline."
|
||||
|
||||
@@ -39,9 +39,10 @@ by aura. The experiments-&-results plane is the run registry in `aura-registry`
|
||||
|
||||
- `runs.jsonl` — the append-only flat store, one `RunReport` per line
|
||||
(`RunManifest` + `RunMetrics`), with a typed read-path (`load`) and best-first
|
||||
ranking (`rank_by`/`optimize`). No live producer writes it today — sweep /
|
||||
walk-forward / mc persist to the family store and `aura run` does not persist —
|
||||
so the flat lib API is retained but selectively live: `rank_by` backs
|
||||
ranking (`rank_by`/`optimize`). No live producer writes it today — the campaign
|
||||
executor (`exec`'s campaign leg, `aura-campaign`, #319) persists to the family
|
||||
store instead, and `exec`'s blueprint leg (single run) does not persist — so
|
||||
the flat lib API is retained but selectively live: `rank_by` backs
|
||||
`aura runs family … rank`, `optimize` backs walk-forward's in-sample step
|
||||
(`aura-campaign` and the registry's own selection helper both call it), while
|
||||
`append`/`load` remain public API with no in-tree caller (a latent surface for
|
||||
@@ -49,14 +50,16 @@ by aura. The experiments-&-results plane is the run registry in `aura-registry`
|
||||
- `families.jsonl` — the family store. A sweep / Monte-Carlo / walk-forward /
|
||||
cross-instrument run persists as a *set of related records*, each a
|
||||
`FamilyRunRecord` (a `RunReport` stamped with `family` + `run` + `kind` +
|
||||
`ordinal`; `FamilyKind ∈ {Sweep, MonteCarlo, WalkForward, CrossInstrument}`).
|
||||
`group_families` re-derives a family from the stored links (re-listable /
|
||||
rankable as a unit — C21). The user-facing `family_id = "{family}-{run}"` handle
|
||||
is **derived** from the stored `family` name plus a per-name `run` index
|
||||
(numeric max+1 — not a content hash, so re-running the same family mints a fresh
|
||||
id). CLI: `aura runs families`, `aura runs family <id> [rank <metric>]`;
|
||||
`aura sweep`/`walkforward`/`mc` persist via `append_family` with an optional
|
||||
`--name`.
|
||||
`ordinal`; `FamilyKind ∈ {Sweep, MonteCarlo, WalkForward, CrossInstrument}`,
|
||||
the last now dead — below). `group_families` re-derives a family from the
|
||||
stored links (re-listable / rankable as a unit — C21). The user-facing
|
||||
`family_id = "{family}-{run}"` handle is **derived** from the stored `family`
|
||||
name plus a per-name `run` index (numeric max+1 — not a content hash, so
|
||||
re-running the same family mints a fresh id). CLI: `aura runs families`,
|
||||
`aura runs family <id> [rank <metric>]`; the campaign executor persists per-cell
|
||||
sweep/walk-forward families via `append_family` under a deterministic,
|
||||
campaign-derived name — no user-supplied `--name` (the retired quintet's
|
||||
naming flag, #319).
|
||||
- `campaign_runs.jsonl` — one thin `CampaignRunRecord` per campaign run (below),
|
||||
over untouched family records.
|
||||
- `blueprints/<hash>.json`, `processes/`, `campaigns/` — the content-addressed
|
||||
@@ -109,12 +112,13 @@ the byte-exact `topology_hash` keeps every debug role untouched (introspection-o
|
||||
the blueprint does not use leaves the id byte-stable. `--content-id` and
|
||||
`--identity-id` are combinable.
|
||||
|
||||
**Cross-instrument generalization.** `FamilyKind::CrossInstrument`: `aura generalize`
|
||||
runs one candidate across an instrument list and persists the M per-instrument runs
|
||||
via `append_family`, each member self-identifying through `RunManifest.instrument`.
|
||||
The generalization score (worst-case R floor + sign-agreement + per-instrument
|
||||
breakdown) is a **recomputable aggregate** over those members, not a persisted
|
||||
family-level record.
|
||||
**Cross-instrument generalization (retired standalone family, #319).** The
|
||||
`FamilyKind::CrossInstrument` variant is now dead: the standalone `aura
|
||||
generalize` verb that used to run one candidate across an instrument list and
|
||||
persist the M per-instrument runs via `append_family` is retired. The
|
||||
surviving cross-instrument grading is `std::generalize`'s campaign-scope
|
||||
computation (below, "Annotators are terminal") — a **recomputable aggregate**
|
||||
over the cells' nominees, not a persisted family-level record.
|
||||
|
||||
**Research-artifact document stores.** `processes/` and `campaigns/` hold two
|
||||
document types (C25 roles 5 / 6b): the **process document** (a named
|
||||
@@ -145,7 +149,7 @@ falls in. Pinned by
|
||||
`referential_tier_accepts_a_kind_correct_axis_over_a_bound_param`
|
||||
(`aura-registry/src/lib.rs`).
|
||||
|
||||
**The campaign executor.** `aura campaign run <file|content-id>` executes a
|
||||
**The campaign executor.** `aura exec <file|content-id>` (#319) executes a
|
||||
campaign (a file is register-then-run sugar; the content id is canonical): a
|
||||
zero-fault referential gate, then the process pipeline. The executable shape is
|
||||
`std::sweep [std::gate]* [std::walk_forward]? [std::monte_carlo]? [std::generalize]?`
|
||||
|
||||
@@ -135,3 +135,36 @@ the mean shows the net/duty-cycle level. Deferred refinements: rendering the min
|
||||
envelope honestly as range bars / OHLC rather than a polyline (#112); a `--width`
|
||||
budget flag and true intra-bucket min/max ordering for the non-default continuous
|
||||
x-mode (#110).
|
||||
|
||||
**Current-state "Single run" + "Families" + "Newcomer" clauses (superseded by
|
||||
the #319 sugar retirement, 2026-07-25):** "**Single run.** `aura run
|
||||
<blueprint.json>` persists every tap the blueprint declares to the trace
|
||||
store under the run's own name, on both shapes: a `bias`-output strategy
|
||||
(`aura-runner::member::run_signal_r`, R-wrapped) and a bare measurement
|
||||
blueprint with ≥1 declared tap but no `bias`
|
||||
(`aura-runner::measure::run_measurement`). A tap-free run writes nothing to
|
||||
`runs/` and its stdout stays byte-identical. The CLI `--trace <NAME>` flag is
|
||||
**retired** on `run` (and on `mc`): it parses but is refused at dispatch
|
||||
(exit 2) — naming a trace is the family / campaign path's job, not a per-run
|
||||
flag.
|
||||
|
||||
**Families.** `aura sweep|walkforward --real <SYM> … --trace <NAME>`
|
||||
persists *each member* via the campaign path: the dissolved verb is
|
||||
translated into a content-addressed process + campaign document whose
|
||||
`presentation.persist_taps` requests the tap vocabulary, run through the one
|
||||
campaign executor; `aura-runner::runner::persist_campaign_traces` writes
|
||||
each member under a depth-2 fan-out `runs/traces/<name>/<cell>/<member>/`.
|
||||
Every written member is independently re-run once in non-reduce trace mode
|
||||
over its own recorded window, and its re-derived metrics are asserted equal
|
||||
to the recorded member's — a **C1 drift alarm** that refuses (exit 1) rather
|
||||
than persist a silently-wrong trace. The **synthetic** sweep / walk-forward
|
||||
path refuses `--trace` (exit 2); **Monte-Carlo is excluded from trace
|
||||
persistence** (`mc --real` refuses `--name`/`--trace`, exit 2 — the
|
||||
real-data R-bootstrap campaign itself runs, but records no per-member
|
||||
family traces; the synthetic `--seeds` family's realization argument, C12,
|
||||
does not carry over to one real series). `TraceStore::ensure_name_free`
|
||||
makes name resolution a total function, refusing cross-kind reuse of one
|
||||
name by both a run and a family." … (Newcomer, tail sentence) "so `aura run`
|
||||
prints summary R-metrics to stdout but writes no on-disk trace — a
|
||||
**chartable** trace today comes from a blueprint that declares taps (single
|
||||
run) or from a `--real … --trace` family campaign."
|
||||
|
||||
@@ -60,31 +60,31 @@ columnar (SoA, C7) `ColumnarTrace` — struct→JSON only — in `aura-engine`
|
||||
`aura-cli` (`render_chart_html` + the vendored `chart-viewer.js`). The engine stays
|
||||
headless.
|
||||
|
||||
**Single run.** `aura run <blueprint.json>` persists every tap the blueprint
|
||||
declares to the trace store under the run's own name, on both shapes: a
|
||||
`bias`-output strategy (`aura-runner::member::run_signal_r`, R-wrapped) and a bare
|
||||
measurement blueprint with ≥1 declared tap but no `bias`
|
||||
(`aura-runner::measure::run_measurement`). A tap-free run writes nothing to `runs/`
|
||||
and its stdout stays byte-identical. The CLI `--trace <NAME>` flag is **retired** on
|
||||
`run` (and on `mc`): it parses but is refused at dispatch (exit 2) — naming a trace
|
||||
is the family / campaign path's job, not a per-run flag.
|
||||
**Single run.** `aura exec <blueprint.json>` (#319) persists every tap the
|
||||
blueprint declares to the trace store under the run's own name, on both
|
||||
shapes: a `bias`-output strategy (`aura-runner::member::run_signal_r`,
|
||||
R-wrapped) and a bare measurement blueprint with ≥1 declared tap but no
|
||||
`bias` (`aura-runner::measure::run_measurement`). A tap-free run writes
|
||||
nothing to `runs/` and its stdout stays byte-identical. `exec` carries no
|
||||
free-standing `--trace <NAME>` flag — naming a trace is the family / campaign
|
||||
path's job, not a per-run flag (the repeatable `--tap TAP=FOLD` selector,
|
||||
#310, only chooses which of the blueprint's own declared taps to subscribe,
|
||||
never names the trace).
|
||||
|
||||
**Families.** `aura sweep|walkforward --real <SYM> … --trace <NAME>` persists *each
|
||||
member* via the campaign path: the dissolved verb is translated into a
|
||||
content-addressed process + campaign document whose `presentation.persist_taps`
|
||||
requests the tap vocabulary, run through the one campaign executor;
|
||||
`aura-runner::runner::persist_campaign_traces` writes each member under a depth-2
|
||||
fan-out `runs/traces/<name>/<cell>/<member>/`. Every written member is
|
||||
independently re-run once in non-reduce trace mode over its own recorded window,
|
||||
and its re-derived metrics are asserted equal to the recorded member's — a **C1
|
||||
drift alarm** that refuses (exit 1) rather than persist a silently-wrong trace. The
|
||||
**synthetic** sweep / walk-forward path refuses `--trace` (exit 2);
|
||||
**Monte-Carlo is excluded from trace persistence** (`mc --real` refuses
|
||||
`--name`/`--trace`, exit 2 — the real-data R-bootstrap campaign itself runs, but
|
||||
records no per-member family traces; the synthetic `--seeds` family's realization
|
||||
argument, C12, does not carry over to one real series).
|
||||
`TraceStore::ensure_name_free` makes name resolution a total function, refusing
|
||||
cross-kind reuse of one name by both a run and a family.
|
||||
**Families.** A campaign document's `presentation.persist_taps` (the closed
|
||||
tap vocabulary, C18/C27) requests traces for each cell's nominee, run through
|
||||
`aura exec <campaign.json|id>` (#319 — a document's `axes` replace the
|
||||
retired `sweep|walkforward --real <SYM> --trace <NAME>` grammar entirely;
|
||||
there is no per-invocation `--trace`/`--name` flag left on any surface):
|
||||
`aura-runner::runner::persist_campaign_traces` writes each member under a
|
||||
depth-2 fan-out `runs/traces/{campaign8}-{run}/<cell>/<member>/`, the name
|
||||
derived deterministically (`derive_trace_name`), never user-chosen. Every
|
||||
written member is independently re-run once in non-reduce trace mode over
|
||||
its own recorded window, and its re-derived metrics are asserted equal to
|
||||
the recorded member's — a **C1 drift alarm** that refuses (exit 1) rather
|
||||
than persist a silently-wrong trace. `TraceStore::ensure_name_free` makes
|
||||
name resolution a total function, refusing cross-kind reuse of one name by
|
||||
both a run and a family.
|
||||
|
||||
**Viewer.** `aura chart <name> [--tap <t>] [--panels]` classifies the name on disk
|
||||
(`TraceStore::name_kind`: top-level `index.json` → a single Run; member subdirs → a
|
||||
@@ -117,10 +117,10 @@ vocabulary (an SMA-cross → `Bias` strategy), `.gitignore`, and a project `CLAU
|
||||
The engine also ships example blueprints under
|
||||
`crates/aura-cli/examples/r_{sma,breakout,channel,meanrev}.json`. Honest gap against
|
||||
the Guarantee's "populated trace immediately": these shipped blueprints are
|
||||
*strategies* (a `bias` output, no declared taps), so `aura run` prints summary
|
||||
*strategies* (a `bias` output, no declared taps), so `aura exec` prints summary
|
||||
R-metrics to stdout but writes no on-disk trace — a **chartable** trace today comes
|
||||
from a blueprint that declares taps (single run) or from a `--real … --trace` family
|
||||
campaign.
|
||||
from a blueprint that declares taps (single run) or from a campaign document
|
||||
requesting `presentation.persist_taps`.
|
||||
|
||||
**Deferred.** Live sink streams *during* a run are not built — taps are
|
||||
buffer-then-drain (collected, then written after the run), and there is no local
|
||||
|
||||
@@ -157,4 +157,81 @@ convert every deliberate template improvement into forced churn of the frozen
|
||||
fixture — the same cross-purpose coupling that rules out regenerating the
|
||||
fixture from the scaffolder.
|
||||
|
||||
**Current-state "Runs and families are built FROM blueprint-data" section
|
||||
(superseded by the #319 sugar retirement, 2026-07-25):** "`aura run
|
||||
<blueprint.json>` loads a serialized **signal** blueprint and emits a
|
||||
`RunReport` **bit-identical** (C1) to its Rust-built twin; the run scaffolding
|
||||
(sinks / broker / data) is supplied **at run**, not serialized. Beyond a
|
||||
single run, the World constructs and orchestrates **families** of harnesses
|
||||
from topology-data:
|
||||
|
||||
- `aura sweep <blueprint.json> --axis <name>=<csv>` → `FamilyKind::Sweep`. A
|
||||
sweep needs an **open** blueprint (a fully-bound one has an empty
|
||||
`param_space`).
|
||||
- `aura mc <blueprint.json> --seeds N` → `FamilyKind::MonteCarlo`, each seed a
|
||||
distinct synthetic price walk drawn disjoint-parallel through the engine
|
||||
`monte_carlo` seam (invariant 1). MC binds no axis, so it needs a **closed**
|
||||
blueprint (the sweep's distinction inverted); an open one returns a named
|
||||
`Err`, rendered exit-2 at the builder boundary — no hidden exit in the pure
|
||||
builder.
|
||||
- `aura walkforward <blueprint.json> --axis <name>=<csv> [--select
|
||||
argmax|plateau:mean|plateau:worst]` → `FamilyKind::WalkForward`:
|
||||
re-optimizes the loaded blueprint's params over the `--axis` grid on each
|
||||
24/12/12 IS window, selects the winner by `sqn_normalized`, runs it
|
||||
out-of-sample. Reduce-mode members are R-measured (`oos_r` the meaningful
|
||||
summary; stitched pip-equity empty, C10).
|
||||
|
||||
The family builders live in `aura-runner::family` (`blueprint_sweep_family` /
|
||||
`blueprint_mc_family` / `blueprint_walkforward_family`). Every member manifest
|
||||
carries the **shared** `topology_hash` (one signal topology, only params
|
||||
vary; `member_key` distinguishes members), and each family stores its
|
||||
blueprint(s) content-addressed so `aura reproduce` re-derives every member
|
||||
bit-identically (`aura-runner::reproduce`, C18). The synthetic-walk DGP is
|
||||
the MC machinery, not trader-grade statistics; a real-data block-bootstrap —
|
||||
and retiring the `synthetic_walk_sources` `len:60`↔warm-up coupling — rides
|
||||
#172."
|
||||
|
||||
**Current-state "Axis discovery" section (superseded by the #319 sugar
|
||||
retirement, 2026-07-25):** "`aura sweep <blueprint.json> --list-axes` lists
|
||||
a loaded blueprint's open sweepable knobs (one `<name>:<kind>` per line,
|
||||
`param_space()` order) and exits; the printed names are exactly what
|
||||
`--axis` binds. Every listed name is **mandatory** on `sweep` /
|
||||
`walkforward` — the blueprint must be fully bound before it runs — so a
|
||||
subset grid is refused with the missing knob named (`BindError::MissingKnob`,
|
||||
`aura-engine`) and there is no default; pin a knob you do not want to vary
|
||||
with a single-value axis. A single `blueprint_axis_probe` (`aura-runner`)
|
||||
single-sources the wrapped probe for the sweep terminal, the MC closed-check,
|
||||
and the listing, so **listed == swept by construction** (and stays so across
|
||||
the harness retirement — the listing tracks whatever the sweep actually
|
||||
resolves). One raw namespace, `<node>.<param>` (a splice path keeps its
|
||||
interior path, e.g. `anchor.sess.period_minutes`), is the only user-facing
|
||||
axis name (#328): `graph introspect --params` and `--list-axes` are
|
||||
line-identical (open params bare, bound params with `default=`) and both
|
||||
print exactly what `--axis` binds, on both sweep routes. The wrapped
|
||||
`<blueprint>.<node>.<param>` form the probe still resolves against
|
||||
internally is retired from the surface, with a translation refusal naming
|
||||
the raw candidate on both intake seams (`--axis` and `campaign validate`).
|
||||
`--trace` on `sweep` / `walkforward` writes per-member traces on the
|
||||
real-data campaign path (depth-2 fan-out, chartable by the printed family
|
||||
handle, #224); the synthetic path still refuses (`run` / `mc` refuse
|
||||
`--trace` outright). The live trace-writer is the campaign
|
||||
`presentation.persist_taps` (`persist_campaign_traces`,
|
||||
`aura-runner::runner`)."
|
||||
|
||||
**Current-state `name`-op gated-intake clause (superseded by the #319 sugar
|
||||
retirement, 2026-07-25):** "Gated by a shared shape check (non-empty, single
|
||||
path segment, no `/`, `\`, `.` or `..`) applied at this op intake **plus
|
||||
every CLI intake that reads an authored blueprint envelope from a file** —
|
||||
`register`, `introspect --content-id <FILE>`, the bare graph-file viewer,
|
||||
`run`, `introspect --params <FILE>`, `sweep --list-axes`, and each of
|
||||
`sweep`/`walkforward`/`mc`/`generalize`'s file-reading entry points
|
||||
(synthetic and `--real` alike) — one class of intake, one gate. The
|
||||
refusal's core sentence (`name_gate_fault_prose`) is byte-uniform across
|
||||
every one of these sites; only the leading context prefix varies — `run`
|
||||
and the bare graph-file viewer prepend `aura: <path>:`, while `register`,
|
||||
the sweep-axis family (`validate_and_register_axes`), and `introspect`
|
||||
prepend bare `aura: `. Store read-back (`reproduce`, `use` resolution,
|
||||
`introspect`/`--params` by content id) stays deliberately ungated — C29: a
|
||||
registered artifact is never retroactively invalidated."
|
||||
|
||||
**[C26, 2026-07-10 (#231): the single-price data weld inside the surviving `wrap_r` scaffolding is retired — input roles bind archive columns by name; the wrapper's remaining R-scaffolding retirement stays #159.]** (From the C24 Forbids clause as of that date; the single-price weld was retired at #231/C26, but `wrap_r` itself survives — its full R-scaffolding retirement stays deferred, #159.)
|
||||
|
||||
@@ -75,34 +75,33 @@ channel). `LinComb` / `CostSum` / `Session` — formerly listed here — entered
|
||||
round-trippable set with #271's typed construction args (see the add-op `args`
|
||||
clause and the data-driven `format_version` below).
|
||||
|
||||
### Runs and families are built FROM blueprint-data
|
||||
### Runs and families are built FROM documents, via `exec` (#319, 2026-07-25)
|
||||
|
||||
`aura run <blueprint.json>` loads a serialized **signal** blueprint and emits a
|
||||
`RunReport` **bit-identical** (C1) to its Rust-built twin; the run scaffolding (sinks
|
||||
/ broker / data) is supplied **at run**, not serialized. Beyond a single run, the
|
||||
World constructs and orchestrates **families** of harnesses from topology-data:
|
||||
`aura exec <blueprint.json>` loads a serialized **signal** blueprint and runs it
|
||||
once, emitting a `RunReport` **bit-identical** (C1) to its Rust-built twin (a
|
||||
no-bias blueprint with ≥1 declared tap runs the measurement leg instead); the
|
||||
run scaffolding (sinks / broker / data) is supplied **at run**, not serialized.
|
||||
Beyond a single run, the World constructs and orchestrates **families** from a
|
||||
**campaign document** (role 6b, [C18](c18-registry.md)/[C25](c25-role-model.md)):
|
||||
`aura exec <campaign.json|id>` drives the document's `strategies[].axes` through
|
||||
the `aura-campaign` executor (`aura_campaign::exec::execute`) — one member per
|
||||
(strategy, instrument, window) cell, the process document's stage pipeline
|
||||
(`std::sweep [std::gate]* [std::walk_forward]? [std::monte_carlo]?
|
||||
[std::generalize]?`) deciding what each cell realizes (grid family, gated
|
||||
survivors, walk-forward roll, MC bootstrap, cross-instrument generalization —
|
||||
C18's cycle-0107/0108 realizations). The one deliberate argv residue,
|
||||
`--override NODE.PARAM=VALUE`, reopens a bound param for the execution without
|
||||
touching the document (blueprint leg: `override_paths` + `reopen_all`; campaign
|
||||
leg: injected as a single-value axis ahead of `validate_campaign_refs`, refusing
|
||||
a collision with a document-declared axis).
|
||||
|
||||
- `aura sweep <blueprint.json> --axis <name>=<csv>` → `FamilyKind::Sweep`. A sweep
|
||||
needs an **open** blueprint (a fully-bound one has an empty `param_space`).
|
||||
- `aura mc <blueprint.json> --seeds N` → `FamilyKind::MonteCarlo`, each seed a
|
||||
distinct synthetic price walk drawn disjoint-parallel through the engine
|
||||
`monte_carlo` seam (invariant 1). MC binds no axis, so it needs a **closed**
|
||||
blueprint (the sweep's distinction inverted); an open one returns a named `Err`,
|
||||
rendered exit-2 at the builder boundary — no hidden exit in the pure builder.
|
||||
- `aura walkforward <blueprint.json> --axis <name>=<csv> [--select
|
||||
argmax|plateau:mean|plateau:worst]` → `FamilyKind::WalkForward`: re-optimizes the
|
||||
loaded blueprint's params over the `--axis` grid on each 24/12/12 IS window,
|
||||
selects the winner by `sqn_normalized`, runs it out-of-sample. Reduce-mode members
|
||||
are R-measured (`oos_r` the meaningful summary; stitched pip-equity empty, C10).
|
||||
|
||||
The family builders live in `aura-runner::family` (`blueprint_sweep_family` /
|
||||
`blueprint_mc_family` / `blueprint_walkforward_family`). Every member manifest carries
|
||||
the **shared** `topology_hash` (one signal topology, only params vary; `member_key`
|
||||
distinguishes members), and each family stores its blueprint(s) content-addressed so
|
||||
`aura reproduce` re-derives every member bit-identically (`aura-runner::reproduce`,
|
||||
C18). The synthetic-walk DGP is the MC machinery, not trader-grade statistics; a
|
||||
real-data block-bootstrap — and retiring the `synthetic_walk_sources` `len:60`↔warm-up
|
||||
coupling — rides #172.
|
||||
Every member manifest carries the **shared** `topology_hash` (one signal
|
||||
topology, only params vary; `member_key` distinguishes members), and the
|
||||
executor stores its blueprint(s) content-addressed so `aura reproduce`
|
||||
re-derives every member bit-identically (C18). The disjoint-parallel synthetic
|
||||
price walks a sweep/MC family draws (`aura_runner::family`) are the family
|
||||
machinery, not trader-grade statistics; a real-data block-bootstrap — and
|
||||
retiring the `synthetic_walk_sources` `len:60`↔warm-up coupling — rides #172.
|
||||
|
||||
### Reproduction identity
|
||||
|
||||
@@ -119,27 +118,24 @@ identity id is introspection-only until a dedup consumer exists.
|
||||
|
||||
### Axis discovery
|
||||
|
||||
`aura sweep <blueprint.json> --list-axes` lists a loaded blueprint's open sweepable
|
||||
knobs (one `<name>:<kind>` per line, `param_space()` order) and exits; the printed
|
||||
names are exactly what `--axis` binds. Every listed name is **mandatory** on `sweep` /
|
||||
`walkforward` — the blueprint must be fully bound before it runs — so a subset grid is
|
||||
refused with the missing knob named (`BindError::MissingKnob`, `aura-engine`) and
|
||||
there is no default; pin a knob you do not want to vary with a single-value axis. A
|
||||
single `blueprint_axis_probe` (`aura-runner`) single-sources the wrapped probe for the
|
||||
sweep terminal, the MC closed-check, and the listing, so **listed == swept by
|
||||
construction** (and stays so across the harness retirement — the listing tracks
|
||||
whatever the sweep actually resolves). One raw namespace, `<node>.<param>` (a splice
|
||||
path keeps its interior path, e.g. `anchor.sess.period_minutes`), is the only
|
||||
user-facing axis name (#328): `graph introspect --params` and `--list-axes` are
|
||||
line-identical (open params bare, bound params with `default=`) and both print
|
||||
exactly what `--axis` binds, on both sweep routes. The wrapped
|
||||
`<blueprint>.<node>.<param>` form the probe still resolves against internally is
|
||||
retired from the surface, with a translation refusal naming the raw candidate on
|
||||
both intake seams (`--axis` and `campaign validate`). `--trace` on
|
||||
`sweep` / `walkforward` writes per-member traces on the real-data campaign path
|
||||
(depth-2 fan-out, chartable by the printed family handle, #224); the synthetic path
|
||||
still refuses (`run` / `mc` refuse `--trace` outright). The live trace-writer is the
|
||||
campaign `presentation.persist_taps` (`persist_campaign_traces`, `aura-runner::runner`).
|
||||
`aura graph introspect --params <blueprint.json|id>` (#328) is the one
|
||||
axis-discovery surface: it lists a loaded blueprint's open knobs (one
|
||||
`<name>:<kind>` per line, `param_space()` order) followed by its bound knobs
|
||||
(`<name>:<kind> default=<value>`) — the campaign-axis namespace
|
||||
`validate_campaign_refs` checks a campaign document's `strategies[].axes`
|
||||
against. Every open name a campaign document's `axes` varies is **mandatory**:
|
||||
a subset is refused with the missing knob named (`BindError::MissingKnob`,
|
||||
`aura-engine`) and there is no default; pin a knob you do not want to vary with
|
||||
a single-value axis. One raw namespace, `<node>.<param>` (a splice path keeps
|
||||
its interior path, e.g. `anchor.sess.period_minutes`), is the only user-facing
|
||||
axis name (#328) — the wrapped `<blueprint>.<node>.<param>` form is retired
|
||||
from the surface, with a did-you-mean refusal naming the raw candidate at the
|
||||
one remaining intake seam, `campaign validate` (#319 retired the standalone
|
||||
`--axis`/`--list-axes` CLI flags this used to also gate). Per-member traces
|
||||
are the campaign document's `presentation.persist_taps` (closed vocabulary,
|
||||
C18 cycle-0109) or, on `exec`'s blueprint leg, the repeatable `--tap
|
||||
TAP=FOLD` selector (#310) over a blueprint's own declared taps (C27); the
|
||||
live trace-writer is `persist_campaign_traces` (`aura-runner::runner`).
|
||||
|
||||
### The construction service (op-script)
|
||||
|
||||
@@ -203,15 +199,15 @@ replayed in order; nodes are referenced **by identifier**, ports as dotted
|
||||
built composite keeps the CLI's own seed default (`"graph"`). Gated by a
|
||||
shared shape check (non-empty, single path segment, no `/`, `\`, `.` or
|
||||
`..`) applied at this op intake **plus every CLI intake that reads an
|
||||
authored blueprint envelope from a file** — `register`, `introspect
|
||||
--content-id <FILE>`, the bare graph-file viewer, `run`, `introspect
|
||||
--params <FILE>`, `sweep --list-axes`, and each of
|
||||
`sweep`/`walkforward`/`mc`/`generalize`'s file-reading entry points
|
||||
(synthetic and `--real` alike) — one class of intake, one gate. The
|
||||
refusal's core sentence (`name_gate_fault_prose`) is byte-uniform across
|
||||
every one of these sites; only the leading context prefix varies —
|
||||
`run` and the bare graph-file viewer prepend `aura: <path>:`, while
|
||||
`register`, the sweep-axis family (`validate_and_register_axes`), and
|
||||
authored blueprint envelope from a file** — `graph register`, `introspect
|
||||
--content-id <FILE>`, the bare graph-file viewer, `introspect --params
|
||||
<FILE>`'s file branch, and `exec`'s blueprint leg (#319; its narrower,
|
||||
envelope-only grammar shares the same `gate_authored_root_name` call
|
||||
directly rather than through the shape-discriminating wrapper) — one
|
||||
class of intake, one gate. The refusal's core sentence
|
||||
(`name_gate_fault_prose`) is byte-uniform across every one of these
|
||||
sites; only the leading context prefix varies — `exec` and the bare
|
||||
graph-file viewer prepend `aura: <path>:`, while `register` and
|
||||
`introspect` prepend bare `aura: `. Store read-back (`reproduce`, `use`
|
||||
resolution, `introspect`/`--params` by content id) stays deliberately
|
||||
ungated — C29: a registered artifact is never retroactively invalidated.
|
||||
@@ -245,8 +241,8 @@ replayed in order; nodes are referenced **by identifier**, ports as dotted
|
||||
recorded observation point (a `Composite.taps` entry, C27), not a boundary output.
|
||||
Name-addressed like every other op (no raw index); tap names are their own
|
||||
namespace (a duplicate refuses). The `finish` gate threads op-declared taps into
|
||||
the built `Composite` (`.with_taps`); a single `aura run` records each, a sweep
|
||||
leaves them inert.
|
||||
the built `Composite` (`.with_taps`); a single `aura exec` (#319) records
|
||||
each, a campaign member run leaves them inert.
|
||||
- `gang` — `{"op":"gang","as":"channel_length","into":["channel_hi.length","channel_lo.length"]}`
|
||||
— fuse two or more sibling params into ONE public knob: the member addresses
|
||||
leave the sweepable param space and `as` replaces them; the bound or swept
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# C25 — The role model: nine authoring roles, cut by artifact + surface + iteration cost: history
|
||||
|
||||
> FROZEN HISTORICAL RECORD. Each block below was true as of its cycle/date stamp and may be superseded; this file is NOT current truth and NOT a grounding surface. Current contract: [c25-role-model.md](c25-role-model.md).
|
||||
|
||||
**Current-state executor-verb-set sentence (owner-minuted 2026-07-21 on #295,
|
||||
delivered by #300; superseded by the #319 sugar retirement, 2026-07-25):**
|
||||
"The executor verb set is settled: `run`, the four thin per-verb generators,
|
||||
and the document verbs validate/introspect/register/show/run/runs — `show`
|
||||
being #300's read-back addition. The verbs' per-verb identity is re-ratified
|
||||
(#300 F8 — reduction, not grammar collapse)."
|
||||
@@ -74,12 +74,14 @@ present but faceless: no addressed verb families yet. Role homes in the project
|
||||
layout and docs-by-role are open (#192).
|
||||
|
||||
**Document-first completion** is the resolved direction on the control-surface
|
||||
amendment (owner-minuted 2026-07-21 on #295), delivered by #300. The executor verb
|
||||
set is settled: `run`, the four thin per-verb generators, and the document verbs
|
||||
validate/introspect/register/show/run/runs — `show` being #300's read-back
|
||||
addition. The verbs' per-verb identity is re-ratified (#300 F8 — reduction, not
|
||||
grammar collapse). A typed-protocol host or MCP face remains demand-driven and
|
||||
unbuilt.
|
||||
amendment (owner-minuted 2026-07-21 on #295), delivered by #300. The executor
|
||||
surface is settled again, one reduction further (#319, 2026-07-25): a single
|
||||
`exec <target>` verb runs both document classes — a campaign (file or content
|
||||
id) and a signal blueprint (single run) — over the document verbs
|
||||
validate/introspect/register/show/runs (`show` #300's read-back addition); the
|
||||
one argv residue is `--override NODE.PARAM=VALUE`, a per-execution bound-param
|
||||
reopen threading through both `exec` legs. A typed-protocol host or MCP face
|
||||
remains demand-driven and unbuilt.
|
||||
|
||||
## See also
|
||||
- [C16](c16-engine-project-split.md) — the game-engine analogy this extends to people
|
||||
@@ -88,3 +90,5 @@ unbuilt.
|
||||
- [C20](c20-strategy-harness.md), [C21](c21-world.md) — the node/harness/World tiers behind the 6a/6b split
|
||||
- [C24](c24-blueprint-data.md) — topology-as-data, the strategy designer's artifact
|
||||
- [C26](c26-input-binding.md) — a role's input contract carried in blueprint data
|
||||
|
||||
> History: [c25-role-model.history.md](c25-role-model.history.md)
|
||||
|
||||
@@ -9,3 +9,8 @@ the #310 `--tap` selector):** "…and the shared `bind_tap_plan`/`BoundTaps`
|
||||
pair called by both declared-tap entry points, `run_signal_r`
|
||||
(`aura-runner::member`) and `run_measurement` (`aura-runner::measure`); both
|
||||
CLI verbs pass a record-all plan."
|
||||
|
||||
**Current-state "single CLI verb" clause (superseded by the #319 sugar
|
||||
retirement, 2026-07-25):** "…both arms of the single CLI verb `aura run`,
|
||||
whose repeatable `--tap TAP=FOLD` selector (#310) makes the `Named`
|
||||
selection data-reachable…"
|
||||
|
||||
@@ -68,8 +68,8 @@ and the roster-enumerating refusal — plus a scalar-typed param schema; all cor
|
||||
entries are param-less today, the seam ships in every entry's build signature),
|
||||
and the shared `bind_tap_plan`/`BoundTaps` pair called by both declared-tap entry
|
||||
points, `run_signal_r` (`aura-runner::member`) and `run_measurement`
|
||||
(`aura-runner::measure`) — both arms of the single CLI verb `aura run`, whose
|
||||
repeatable `--tap TAP=FOLD` selector (#310) makes the `Named` selection
|
||||
(`aura-runner::measure`) — both arms of the single CLI verb `aura exec` (#319),
|
||||
whose repeatable `--tap TAP=FOLD` selector (#310) makes the `Named` selection
|
||||
data-reachable: no flag keeps the record-all default, any flag replaces the
|
||||
plan entirely (unlisted taps stay unbound/inert). The boundary is thereby
|
||||
fixed in place: *selecting* a subscription is run-mode authority, exercised
|
||||
|
||||
+11
-11
@@ -33,7 +33,7 @@ A strategy's primary, backtestable DAG output: one signed, bounded `f64 ∈ [-1,
|
||||
|
||||
### blueprint
|
||||
**Avoid:** —
|
||||
The param-generic, input-role-generic graph-as-data produced by running a Rust builder; it carries free numeric params and free input roles before bootstrap. Bootstrapped into a frozen instance by binding params + data + seed. Registered and inspected headless (`aura graph register`, `aura graph introspect --params` — the raw `param_space` namespace campaign axes bind against; cycle 0107/#196); a *bound* param is an overridable **default** — a sweep axis naming it re-opens it per family (#246), while `run` uses it as-is.
|
||||
The param-generic, input-role-generic graph-as-data produced by running a Rust builder; it carries free numeric params and free input roles before bootstrap. Bootstrapped into a frozen instance by binding params + data + seed. Registered and inspected headless (`aura graph register`, `aura graph introspect --params` — the raw `param_space` namespace campaign axes bind against; cycle 0107/#196); a *bound* param is an overridable **default** — a sweep axis naming it re-opens it per family (#246), while a plain `exec` uses it as-is (#319).
|
||||
|
||||
### blueprint label
|
||||
**Avoid:** —
|
||||
@@ -41,7 +41,7 @@ A registry-level name pointing at a registered blueprint's content id (`graph re
|
||||
|
||||
### bootstrap
|
||||
**Avoid:** —
|
||||
The distinct, recursive construction phase that binds `(blueprint + param-set + data bindings + seed)` into a frozen instance — buffers sized, topology fixed. The explicit name for the "wiring / graph build" that C7/C12 reference — the construction/compilation sense. Disambiguation: the *statistical* moving-block bootstrap of a trade-R series (`r_bootstrap`, `RBootstrap`) is a different thing that keeps its statistics name — it appears as the deflation null, the `aura mc` R path, and since 0108 the `std::monte_carlo` stage's `stage bootstrap` annotation; context (construction vs annotation) disambiguates.
|
||||
The distinct, recursive construction phase that binds `(blueprint + param-set + data bindings + seed)` into a frozen instance — buffers sized, topology fixed. The explicit name for the "wiring / graph build" that C7/C12 reference — the construction/compilation sense. Disambiguation: the *statistical* moving-block bootstrap of a trade-R series (`r_bootstrap`, `RBootstrap`) is a different thing that keeps its statistics name — it appears as the deflation null and, since 0108, the `std::monte_carlo` stage's `stage bootstrap` annotation (the campaign executor's one R-bootstrap path, #319); context (construction vs annotation) disambiguates.
|
||||
|
||||
### bot
|
||||
**Avoid:** —
|
||||
@@ -53,11 +53,11 @@ A downstream consumer node, never part of the strategy: the signal-quality side
|
||||
|
||||
### campaign document
|
||||
**Avoid:** experiment doc, campaign file
|
||||
The role-6b research artifact (#188/#189): persisted experiment intent as closed-vocabulary canonical JSON — instruments × windows × strategy refs (by `content id` or `identity id`) × per-strategy param axes (each axis declares its `ScalarKind` once over bare values) × a process reference (content-id-only) × data-level presentation (taps to persist — the closed `tap` vocabulary — and tables to emit). Authored, validated, and executed headless (`aura campaign validate|introspect|register|show|run|runs` — `show` prints a registered document's canonical bytes back, #300), content-addressed beside blueprints in the registry store; carries P1 control constructs (bounded axes, gates, ladders) as intent, executed by `aura campaign run` (v2 pipeline shape `std::sweep [std::gate]* [std::walk_forward]? [std::monte_carlo]? [std::generalize]?` — the two annotators terminal, cycles 0107/0108, #198/#200) into a `campaign run` realization. `std::sweep`'s own selection group (`metric`+`select`) is optional, all-or-nothing, and permitted only as the pipeline's terminal stage when omitted (a selection-free sweep, #210).
|
||||
The role-6b research artifact (#188/#189): persisted experiment intent as closed-vocabulary canonical JSON — instruments × windows × strategy refs (by `content id` or `identity id`) × per-strategy param axes (each axis declares its `ScalarKind` once over bare values) × a process reference (content-id-only) × data-level presentation (taps to persist — the closed `tap` vocabulary — and tables to emit). Authored headless (`aura campaign validate|introspect|register|runs|show` — `show` prints a registered document's canonical bytes back, #300), content-addressed beside blueprints in the registry store; carries P1 control constructs (bounded axes, gates, ladders) as intent, executed by `aura exec` (#319; v2 pipeline shape `std::sweep [std::gate]* [std::walk_forward]? [std::monte_carlo]? [std::generalize]?` — the two annotators terminal, cycles 0107/0108, #198/#200) into a `campaign run` realization. `std::sweep`'s own selection group (`metric`+`select`) is optional, all-or-nothing, and permitted only as the pipeline's terminal stage when omitted (a selection-free sweep, #210).
|
||||
|
||||
### campaign run
|
||||
**Avoid:** campaign execution record, realized campaign
|
||||
One execution of a `campaign document` (`aura campaign run <file|content id>`; a file is register-then-run sugar — the content id is the canonical address). Realized once per (strategy, instrument, window) cell in doc order and recorded as a thin `CampaignRunRecord` line in the registry's `campaign_runs.jsonl` — linking the per-stage family ids, gate survivor ordinals, `stage bootstrap` annotations, and (campaign-scope, per strategy × window) `generalizations` entries (`generalization` with `worst_case`/`sign_agreement`/`per_instrument`, plus `winners` params and `missing` instruments on shortfall) over untouched family records, run-counted per campaign id. A cell whose gate leaves no survivors records its realized prefix and the run exits 0: a null result is a valid research result. Deterministic from doc + stores + data (C1): deflation and bootstrap nulls seed from the doc's `seed`. When the document requests `persist_taps`, the record's sparse `trace_name` (`"{campaign8}-{run}"`) points at the TraceStore family holding each nominee cell's persisted taps (0109/#201). NB the stdout emit wraps each record as `{"campaign_run": …}`; the stored `campaign_runs.jsonl` line is the bare record.
|
||||
One execution of a `campaign document` (`aura exec <file|content id>`, #319; a file is register-then-run sugar — the content id is the canonical address). Realized once per (strategy, instrument, window) cell in doc order and recorded as a thin `CampaignRunRecord` line in the registry's `campaign_runs.jsonl` — linking the per-stage family ids, gate survivor ordinals, `stage bootstrap` annotations, and (campaign-scope, per strategy × window) `generalizations` entries (`generalization` with `worst_case`/`sign_agreement`/`per_instrument`, plus `winners` params and `missing` instruments on shortfall) over untouched family records, run-counted per campaign id. A cell whose gate leaves no survivors records its realized prefix and the run exits 0: a null result is a valid research result. Deterministic from doc + stores + data (C1): deflation and bootstrap nulls seed from the doc's `seed`. When the document requests `persist_taps`, the record's sparse `trace_name` (`"{campaign8}-{run}"`) points at the TraceStore family holding each nominee cell's persisted taps (0109/#201). NB the stdout emit wraps each record as `{"campaign_run": …}`; the stored `campaign_runs.jsonl` line is the bare record.
|
||||
|
||||
### cdylib
|
||||
**Avoid:** —
|
||||
@@ -89,7 +89,7 @@ A composable downstream **C9 graph of cost nodes**, in **R**, that **approximate
|
||||
|
||||
### cross-instrument generalization
|
||||
**Avoid:** cross-symbol pooling, pooled generalization
|
||||
The validation read that grades how consistently one *brought* candidate holds across a set of instruments, scored on its weakest one — the across-instrument axis of the anti-false-discovery discipline. Realised by `aura generalize` and, since 0108 (#200), by the `std::generalize` process stage at campaign scope (per (strategy, window) over the cells' nominees across instruments, recorded in the `campaign run`'s `generalizations`); an aggregator (a recomputable family score), never a selector that picks a winner.
|
||||
The validation read that grades how consistently one *brought* candidate holds across a set of instruments, scored on its weakest one — the across-instrument axis of the anti-false-discovery discipline. Realised by the `std::generalize` process stage at campaign scope (since 0108, #200; per (strategy, window) over the cells' nominees across instruments, recorded in `CampaignRunRecord.generalizations`, executed via `aura exec`, #319) — the standalone `aura generalize` verb that used to persist its own per-instrument family is retired (#319: `FamilyKind::CrossInstrument` is now dead); an aggregator (a recomputable family score), never a selector that picks a winner.
|
||||
|
||||
### cycle
|
||||
**Avoid:** —
|
||||
@@ -203,7 +203,7 @@ The recorded chance that a deflated sweep winner is noise rather than edge — a
|
||||
|
||||
### plateau selection
|
||||
**Avoid:** plateau-over-peak (as a noun)
|
||||
A selection objective that argmaxes the neighbourhood-smoothed metric surface (mean or worst-case) rather than the bare in-sample peak, preferring a robust parameter plateau to a lucky spike. Opt-in via `--select plateau:mean|plateau:worst`; the default selection stays a bare argmax.
|
||||
A selection objective that argmaxes the neighbourhood-smoothed metric surface (mean or worst-case) rather than the bare in-sample peak, preferring a robust parameter plateau to a lucky spike. Opt-in via a process document's `std::sweep`/`std::walk_forward` stage `"select": "plateau:mean"|"plateau:worst"` field; the default selection stays a bare argmax.
|
||||
|
||||
### playground
|
||||
**Avoid:** —
|
||||
@@ -331,15 +331,15 @@ The harness's structural parameterization — which strategy, instrument(s), bro
|
||||
|
||||
### sweep
|
||||
**Avoid:** param-sweep, parameter sweep
|
||||
An orchestration axis varying tuning params (grid or random) within a fixed structure. The inner, param-tuning loop, distinct from the structural experiment matrix. On a loaded blueprint every open knob (`--list-axes`) is **required** — a subset is refused with the missing knob named; pin an unwanted knob with a single-value axis (`--axis name=<one-value>`), there is no default. One raw namespace, `<node>.<param>` (a splice path keeps its interior path), is the only axis name (#328): `graph introspect --params` and `aura sweep --list-axes` are line-identical (open params bare, bound params with `default=`), and `--axis` accepts exactly that raw form on both sweep routes — the older `<blueprint>.<node>.<param>` wrapped form (e.g. `graph.fast.length`) is retired from the surface; naming it on `--axis` refuses (`axis names are raw node.param paths — use "fast.length" …`), and quoting it in a campaign document gets a did-you-mean toward the raw candidate. The `aura sweep` CLI verb is now thin sugar over the `campaign document` path — its blueprint form (`<bp.json> --real`) translates to a generated, content-addressed campaign run through the one executor (#210); the built-in `--strategy` sweep surface was retired by #159 (its hard-wired harnesses removed) — sweep now runs from a blueprint + `--axis`, and a retired `--strategy` token falls to the generic usage error. A ganged pair contributes ONE axis.
|
||||
An orchestration axis varying tuning params (grid or random) within a fixed structure. The inner, param-tuning loop, distinct from the structural experiment matrix. On a loaded blueprint every open knob (`graph introspect --params`) is **required** on a campaign document's `strategies[].axes` — a subset is refused with the missing knob named; pin an unwanted knob with a single-value axis (one value in its `values` list), there is no default. One raw namespace, `<node>.<param>` (a splice path keeps its interior path), is the only axis name (#328): `graph introspect --params` and a campaign axis key are line-identical (open params bare, bound params with `default=`) — the older `<blueprint>.<node>.<param>` wrapped form (e.g. `graph.fast.length`) is retired from the surface; naming it in a campaign document gets a did-you-mean toward the raw candidate, the one remaining intake seam (#319 retired the standalone `--axis`/`--list-axes` CLI flags along with the blueprint-form `aura sweep` verb, whose own `--strategy` built-in surface #159 had already retired). Realised as a campaign document's axes (#210), executed through the one `aura exec` executor (#319). A ganged pair contributes ONE axis.
|
||||
|
||||
### tap
|
||||
**Avoid:** probe, monitor, scope (for the observation slot — "probe" is taken by the sweep-terminal `blueprint_axis_probe` sense; the #77 `Recorder`→`Probe` rename was retired, 2026-07-21)
|
||||
A named recorded stream produced by a recording `sink` — the addressable label (e.g. `equity`, `net_r_equity`) under which one sink's per-cycle output is persisted as a columnar (SoA) `ColumnarTrace` and selected for charting via `--tap`. Distinct from the `sink` node that emits it (a tap is the stream, the sink is the role) and from a whole recorded run (a bundle of taps); taps fire at their own cadences and are fused only by joining on the recorded timestamp, never by positional index. In a `campaign document`, `persist_taps` names taps from the CLOSED vocabulary `equity | exposure | r_equity | net_r_equity` (`tap_vocabulary`, 0109/#201) — a new observable is a new vocabulary entry or an authored blueprint sink, never an open node-path namespace. Persisted taps are charted by the printed handle: a campaign run's via the record's `trace_name`, a `sweep`/`walkforward --trace` family's via the family handle the run prints (`aura chart <handle>`; its members are keyed `<cell>/<member>` in the chart) — or, equivalently, by the `--trace <NAME>` the user chose, when `NAME` uniquely resolves against the recorded campaign documents (#238; a name reused across runs refuses rather than guessing).
|
||||
A named recorded stream produced by a recording `sink` — the addressable label (e.g. `equity`, `net_r_equity`) under which one sink's per-cycle output is persisted as a columnar (SoA) `ColumnarTrace` and selected for charting via `--tap`. Distinct from the `sink` node that emits it (a tap is the stream, the sink is the role) and from a whole recorded run (a bundle of taps); taps fire at their own cadences and are fused only by joining on the recorded timestamp, never by positional index. In a `campaign document`, `persist_taps` names taps from the CLOSED vocabulary `equity | exposure | r_equity | net_r_equity` (`tap_vocabulary`, 0109/#201) — a new observable is a new vocabulary entry or an authored blueprint sink, never an open node-path namespace. Persisted taps are charted by the printed handle: a campaign run's deterministic `trace_name` (`"{campaign8}-{run}"`, never a user-chosen name since #319) charts its whole family (`aura chart <handle>`; members keyed `<cell>/<member>` in the chart) — or, equivalently, a bare campaign id/name that uniquely resolves against the recorded campaign documents (#238; a name reused across runs refuses rather than guessing).
|
||||
|
||||
Since C27 (#282) the word also names a second, author-facing sense: a **declared tap** — a `Composite.taps` entry `Tap { name, from: {node, field} }` a hand-authored blueprint declares on an interior output wire, the output-side twin of an `input_role`. It is a pure declaration (no channel endpoint); the harness binds it run-mode-aware (a single `aura run` constructs a recorder at each and persists the series through the trace store; a sweep leaves it unbound and inert). This is an OPEN, per-blueprint author-declared name — distinct from the CLOSED campaign `persist_taps` vocabulary above, which selects among fixed observables of the standard R-harness. Both senses land as `ColumnarTrace`s in the same trace store; the closed vocabulary is what a `campaign document` selects, the declared tap is what a blueprint author names.
|
||||
Since C27 (#282) the word also names a second, author-facing sense: a **declared tap** — a `Composite.taps` entry `Tap { name, from: {node, field} }` a hand-authored blueprint declares on an interior output wire, the output-side twin of an `input_role`. It is a pure declaration (no channel endpoint); the harness binds it run-mode-aware (a single `aura exec` constructs a recorder at each and persists the series through the trace store; a campaign member run leaves it unbound and inert). This is an OPEN, per-blueprint author-declared name — distinct from the CLOSED campaign `persist_taps` vocabulary above, which selects among fixed observables of the standard R-harness. Both senses land as `ColumnarTrace`s in the same trace store; the closed vocabulary is what a `campaign document` selects, the declared tap is what a blueprint author names.
|
||||
|
||||
Since #283 what CONSUMES a declared tap is itself declared per run by a **tap plan**: a subscription is either `Named { label, params }` — resolved against a layered **fold registry** whose core vocabulary is `record | count | sum | mean | min | max | first | last`, each entry carrying a doc line (the help surface and the roster-enumerating refusal) and a scalar-typed param schema (all core entries param-less; growth is a new Rust entry per C25, and higher layers register entries without touching the core) — or `Live(closure)`, the single non-data variant (an in-process consumer with consumer-owned loss policy). `record` streams the full series to the trace store at constant memory (no buffer-then-drain); folds keep an O(1) accumulator and land one summary row at finalize. Both declared-tap entry points are arms of the single verb `aura run` (`aura measure` is the post-hoc IC analysis over already-persisted traces and constructs no tap plan); `aura run` subscribes every declared tap to `record` by default, and its repeatable `--tap TAP=FOLD` selector (#310) replaces that default with an explicit plan — only listed taps are bound, unlisted taps stay unbound/inert. A fold's one summary row is emitted at finalize and stamped with the instant of the last contributing (warm) value — `first` alone pins the first contributing instant, and `min`/`max` deliberately do not carry the extremum's instant (ratified #335).
|
||||
Since #283 what CONSUMES a declared tap is itself declared per run by a **tap plan**: a subscription is either `Named { label, params }` — resolved against a layered **fold registry** whose core vocabulary is `record | count | sum | mean | min | max | first | last`, each entry carrying a doc line (the help surface and the roster-enumerating refusal) and a scalar-typed param schema (all core entries param-less; growth is a new Rust entry per C25, and higher layers register entries without touching the core) — or `Live(closure)`, the single non-data variant (an in-process consumer with consumer-owned loss policy). `record` streams the full series to the trace store at constant memory (no buffer-then-drain); folds keep an O(1) accumulator and land one summary row at finalize. Both declared-tap entry points are arms of the single verb `aura exec` (#319; `aura measure` is the post-hoc IC analysis over already-persisted traces and constructs no tap plan); `aura exec` subscribes every declared tap to `record` by default, and its repeatable `--tap TAP=FOLD` selector (#310) replaces that default with an explicit plan — only listed taps are bound, unlisted taps stay unbound/inert. A fold's one summary row is emitted at finalize and stamped with the instant of the last contributing (warm) value — `first` alone pins the first contributing instant, and `min`/`max` deliberately do not carry the extremum's instant (ratified #335).
|
||||
|
||||
### topology hash
|
||||
**Avoid:** —
|
||||
@@ -355,7 +355,7 @@ The **optional** documented pre-trade-gate seam in the execution chain (`stop-ru
|
||||
|
||||
### walk-forward
|
||||
**Avoid:** —
|
||||
An orchestration axis: rolling in-sample optimize + out-of-sample test across moving windows, stitched into one out-of-sample verdict plus parameter stability. As with `sweep`, every open knob named by `--list-axes` is **required** on a loaded blueprint — a subset is refused with the missing knob named; pin one with a single-value axis, there is no default. The `aura walkforward <blueprint.json> --real <sym> --axis <name>=<csv> …` CLI verb is thin sugar over the `campaign document` path — translated to a generated campaign (`std::sweep → std::walk_forward`) run through the one executor (#210; blueprint-generic over arbitrary blueprints and axes since #220).
|
||||
An orchestration axis: rolling in-sample optimize + out-of-sample test across moving windows, stitched into one out-of-sample verdict plus parameter stability. As with `sweep`, every open knob a campaign document's axes name is **required** — a subset is refused with the missing knob named; pin one with a single-value axis, there is no default. Realised as a campaign document's `std::sweep → std::walk_forward` process pipeline (#210; blueprint-generic over arbitrary blueprints and axes since #220), executed through the one `aura exec` executor (#319) — the standalone `aura walkforward` CLI verb is retired.
|
||||
|
||||
### World
|
||||
**Avoid:** —
|
||||
|
||||
@@ -144,9 +144,10 @@ command sequences.
|
||||
`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
|
||||
(`aura graph build`); `aura exec blueprints/third-candle-long.json` (#319)
|
||||
smoke-runs it over the synthetic stream — a real-data window over `GER40`
|
||||
between two Unix-millisecond bounds is a one-cell campaign document (step
|
||||
4) → 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
|
||||
@@ -165,7 +166,7 @@ command sequences.
|
||||
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>`
|
||||
registered under `blueprints/`. `aura exec <content-id>` (#319)
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user