From 7aac09d49e030d2218ec945bf863098c60633a8c Mon Sep 17 00:00:00 2001 From: claude Date: Sun, 26 Jul 2026 14:25:26 +0200 Subject: [PATCH] =?UTF-8?q?fieldtest:=20harvest=202026-07-26=20=E2=80=94?= =?UTF-8?q?=204=20examples,=202=20bugs=20/=204=20friction=20/=201=20spec-g?= =?UTF-8?q?ap=20/=204=20working?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Source-blind per-cycle fieldtest over the sweep's four axes: the declared-tap discovery loop closes in three commands without provoked refusals; fixed{distance} is campaign-reachable with honest R compression beside the vol regime; the C14 exit partition branches reliably across sixteen probes; reference-semantics hashes agree between record line and trace index across bare/no-op/substantive overrides. Findings routed: both bugs fixed in-cycle (next commit), frictions bundled as #345, the trace-overwrite spec gap consolidated onto #311, the guide-rot instance evidenced on #344. --- fieldtests/harvest-2026-07-26/README.md | 49 ++++ fieldtests/harvest-2026-07-26/TRANSCRIPT.md | 251 ++++++++++++++++++ .../hv_1_blend_taps.ops.json | 23 ++ .../hv_1b_unnamed_taps.ops.json | 121 +++++++++ .../hv_1c_named_notaps.ops.json | 115 ++++++++ .../harvest-2026-07-26/hv_2_cross.ops.json | 14 + .../harvest-2026-07-26/hv_3_exec_refusals.sh | 48 ++++ .../harvest-2026-07-26/hv_4_flat.ops.json | 11 + fieldtests/harvest-2026-07-26/lab/.gitignore | 1 + fieldtests/harvest-2026-07-26/lab/Aura.toml | 4 + fieldtests/harvest-2026-07-26/lab/CLAUDE.md | 24 ++ .../lab/blueprints/hv-cross.json | 1 + .../lab/blueprints/hv-flat.json | 1 + .../blueprints/hv_2_campaign_two_regimes.json | 24 ++ .../lab/blueprints/hv_2_process_sweep.json | 9 + .../blueprints/hv_4_campaign_zero_trade.json | 44 +++ .../lab/blueprints/ma-blend.json | 1 + .../lab/blueprints/notaps.json | 1 + .../lab/blueprints/signal.json | 1 + .../lab/blueprints/unnamed.json | 1 + 20 files changed, 744 insertions(+) create mode 100644 fieldtests/harvest-2026-07-26/README.md create mode 100644 fieldtests/harvest-2026-07-26/TRANSCRIPT.md create mode 100644 fieldtests/harvest-2026-07-26/hv_1_blend_taps.ops.json create mode 100644 fieldtests/harvest-2026-07-26/hv_1b_unnamed_taps.ops.json create mode 100644 fieldtests/harvest-2026-07-26/hv_1c_named_notaps.ops.json create mode 100644 fieldtests/harvest-2026-07-26/hv_2_cross.ops.json create mode 100644 fieldtests/harvest-2026-07-26/hv_3_exec_refusals.sh create mode 100644 fieldtests/harvest-2026-07-26/hv_4_flat.ops.json create mode 100644 fieldtests/harvest-2026-07-26/lab/.gitignore create mode 100644 fieldtests/harvest-2026-07-26/lab/Aura.toml create mode 100644 fieldtests/harvest-2026-07-26/lab/CLAUDE.md create mode 100644 fieldtests/harvest-2026-07-26/lab/blueprints/hv-cross.json create mode 100644 fieldtests/harvest-2026-07-26/lab/blueprints/hv-flat.json create mode 100644 fieldtests/harvest-2026-07-26/lab/blueprints/hv_2_campaign_two_regimes.json create mode 100644 fieldtests/harvest-2026-07-26/lab/blueprints/hv_2_process_sweep.json create mode 100644 fieldtests/harvest-2026-07-26/lab/blueprints/hv_4_campaign_zero_trade.json create mode 100644 fieldtests/harvest-2026-07-26/lab/blueprints/ma-blend.json create mode 100644 fieldtests/harvest-2026-07-26/lab/blueprints/notaps.json create mode 100644 fieldtests/harvest-2026-07-26/lab/blueprints/signal.json create mode 100644 fieldtests/harvest-2026-07-26/lab/blueprints/unnamed.json diff --git a/fieldtests/harvest-2026-07-26/README.md b/fieldtests/harvest-2026-07-26/README.md new file mode 100644 index 0000000..e70f928 --- /dev/null +++ b/fieldtests/harvest-2026-07-26/README.md @@ -0,0 +1,49 @@ +# Fieldtest corpus — harvest 2026-07-26 + +Downstream-consumer fixtures for the harvest cycle (`77ad046..f108291`): the +declared-tap discovery view, the `fixed{distance}` risk regime, `exec`'s +refusal families, and the zero-trade notes / override identity. + +`TRANSCRIPT.md` is the recorded session — what was written first, what each +command said, and where the two diverged. The findings drawn from it live in +`docs/specs/fieldtest-harvest-2026-07-26.md` (git-ignored working file). + +## Layout + +| Path | What | +|---|---| +| `hv_1_blend_taps.ops.json` | crossover ⊕ momentum blended through `LinComb`, two declared taps | +| `hv_1b_unnamed_taps.ops.json` | same graph without the `name` op (default root name `"graph"`) | +| `hv_1c_named_notaps.ops.json` | same graph without the `tap` ops | +| `hv_2_cross.ops.json` | SMA crossover with `fast.length` left open (the campaign axis) | +| `hv_3_exec_refusals.sh` | replayable matrix of sixteen `aura exec` refusals | +| `hv_4_flat.ops.json` | deliberately flat bias (`Const 0.0 → Bias`) — never trades | +| `lab/` | a plain `aura new lab` scaffold holding the built blueprints and the process / campaign documents (`lab/runs/` is gitignored) | + +## Replaying + +From this directory, with the workspace built (`cargo build --workspace`) and +the resulting `aura` on `PATH`: + +```sh +cd lab +aura graph build < ../hv_1_blend_taps.ops.json > blueprints/ma-blend.json +aura graph build < ../hv_1b_unnamed_taps.ops.json > blueprints/unnamed.json +aura graph build < ../hv_1c_named_notaps.ops.json > blueprints/notaps.json +aura graph build < ../hv_2_cross.ops.json > blueprints/hv-cross.json +aura graph build < ../hv_4_flat.ops.json > blueprints/hv-flat.json +aura graph register blueprints/ma-blend.json --name ma-blend +aura graph register blueprints/hv-cross.json --name hv-cross +aura graph register blueprints/hv-flat.json --name hv-flat +aura process register blueprints/hv_2_process_sweep.json + +aura graph introspect --taps blueprints/ma-blend.json +aura exec blueprints/ma-blend.json --tap spread=mean --tap blend=record +aura exec blueprints/hv_2_campaign_two_regimes.json # two stop regimes, real GER40 data +aura exec blueprints/hv_4_campaign_zero_trade.json # the benign zero-trade cell +sh ../hv_3_exec_refusals.sh +``` + +The blueprints under `lab/blueprints/` are already built and registered at the +content ids the campaign documents reference; rebuilding them reproduces those +ids byte-identically. diff --git a/fieldtests/harvest-2026-07-26/TRANSCRIPT.md b/fieldtests/harvest-2026-07-26/TRANSCRIPT.md new file mode 100644 index 0000000..1388cbd --- /dev/null +++ b/fieldtests/harvest-2026-07-26/TRANSCRIPT.md @@ -0,0 +1,251 @@ +# Fieldtest transcript — harvest 2026-07-26 + +Everything below was run against `target/debug/aura` built from the worktree at +`f108291` (`cargo build --workspace`, exit 0). The project is `lab/`, a plain +`aura new lab` scaffold; `lab/runs/` is gitignored by the scaffold's own +`.gitignore` and is not part of the corpus. + +Reading order: each section is one axis of the cycle, and each records what was +written first, what the command said, and where the two diverged. + +--- + +## Axis 1 — the declared-tap discovery loop + +Task: *a colleague hands you `ma-blend.json`; find out what it measures, +subscribe one tap to a summary fold and one to the full series, and run it.* + +Fixture: `hv_1_blend_taps.ops.json` — an SMA crossover spread and a +price-vs-slow momentum term blended through `LinComb` (`args` arity 2, per the +guide's new §1 example), two declared taps, a `name` and a `doc` op. + +``` +$ aura graph build < ../hv_1_blend_taps.ops.json > blueprints/ma-blend.json +$ aura graph introspect --taps blueprints/ma-blend.json +spread spread_node.value F64 +blend combo.value F64 +``` + +Built and introspected on the first try — the op-script needed no correction. +The same view by content id, after registering: + +``` +$ aura graph register blueprints/ma-blend.json --name ma-blend +registered blueprint ab84251518597f5d06df8fbbaf82572dd5362e06d18af9d33e265e3cc44bf7c6 (…) +label "ma-blend" -> ab84251518597f5d06df8fbbaf82572dd5362e06d18af9d33e265e3cc44bf7c6 +$ aura graph introspect --taps ab84251518597f5d06df8fbbaf82572dd5362e06d18af9d33e265e3cc44bf7c6 +spread spread_node.value F64 +blend combo.value F64 +``` + +The two edge cases, via `hv_1b_unnamed_taps.ops.json` (the same graph with the +`name` op removed) and `hv_1c_named_notaps.ops.json` (named, no taps): + +``` +$ aura graph introspect --taps blueprints/unnamed.json # default root name "graph" +spread spread_node.value F64 +blend combo.value F64 +exit=0 +$ aura graph introspect --taps blueprints/notaps.json +aura: note: blueprints/notaps.json declares no taps +exit=0 +$ aura graph introspect --taps e22a7524f4d41b9e957cb9a2eae15e7fc27488fc9acbd3bd32f06d7ae52b87a3 +aura: note: e22a7524…b87a3 declares no taps +exit=0 +``` + +Subscribing and running, with the fold roster discovered rather than guessed +(`aura graph introspect --folds` lists all eight with a doc line each): + +``` +$ aura exec blueprints/ma-blend.json --tap spread=mean --tap blend=record +{"manifest":{…,"topology_hash":"ab8425…f7c6",…},"metrics":{…}} +exit=0 +$ cat runs/traces/ma-blend/spread.json +{"tap":"spread","kinds":["F64"],"ts":[18],"columns":[[0.00001148148148140867]]} +$ head -c 200 runs/traces/ma-blend/blend.json +{"tap":"blend","kinds":["F64"],"ts":[10,11,…,18],"columns":[[-0.00124…,…,0.004526…]]} +``` + +`mean` landed exactly one row stamped at the last warm instant, `record` the +full nine-row series — as C27 describes. The recovery half still works and is +now redundant rather than load-bearing: + +``` +$ aura exec blueprints/ma-blend.json --tap spred=mean +aura: the tap plan names 'spred', but the blueprint declares no such tap — declared taps: spread, blend +exit=1 +$ aura exec blueprints/ma-blend.json --tap spread=median +aura: unknown fold 'median' — available: count, first, last, max, mean, min, record, sum +exit=1 +``` + +**No refusal was needed to learn a real tap name.** Axis 1 answered yes. + +--- + +## Axis 2 — the fixed-stop campaign regime + +Task: *compare a constant 10-point stop against a volatility stop on the same +SMA-crossover signal over a month of GER40.* + +Fixtures: `hv_2_cross.ops.json` (crossover, `fast.length` left open), +`hv_2_process_sweep.json` (one `std::sweep`, `sqn`/`argmax`), +`hv_2_campaign_two_regimes.json` (`risk: [vol{3, 2.0}, fixed{10.0}]`, +`fast.length ∈ {5,10,20}`, GER40, 2024-09-01 → 2024-10-01). + +All three validate tiers passed on the first authoring, with no iteration: + +``` +$ aura campaign validate blueprints/hv_2_campaign_two_regimes.json +campaign document valid (intrinsic): 1 strategy(ies), 1 axes (3 points), 1 instrument(s), 1 window(s), 2 regime(s) — 2 cell(s) +campaign document valid (referential): all references resolve, axes are in the param space +campaign document valid (executable): pipeline shape and static guards pass +``` + +``` +$ aura exec blueprints/hv_2_campaign_two_regimes.json +… 6 member lines, 2 selection_report lines, 1 campaign_run line … +aura: campaign run 0 recorded: 2 cells +aura: traces persisted: c6ffe19d-0 (1 tap(s) x 2 cell(s)) +exit=0 +``` + +Per-member readout (`family_id`, stamped stop knobs, R metrics): + +| family_id | stamped stop | n_trades | E[R] | sqn | avg_win_r | avg_loss_r | +|---|---|---|---|---|---|---| +| `c6ffe19d-0-GER40-w0-r0-s0-0` (fast=5) | `stop_length=3, stop_k=2.0` | 1781 | −0.0306 | −0.508 | 2.239 | −0.974 | +| `c6ffe19d-0-GER40-w0-r0-s0-0` (fast=10) | `stop_length=3, stop_k=2.0` | 1604 | 0.0360 | 0.432 | 2.488 | −1.104 | +| `c6ffe19d-0-GER40-w0-r0-s0-0` (fast=20) | `stop_length=3, stop_k=2.0` | 1648 | 0.1038 | 1.238 | 2.331 | −1.115 | +| `c6ffe19d-0-GER40-w0-r1-s0-0` (fast=5) | `stop_distance=10.0` | 1542 | −0.0439 | −0.994 | 1.646 | −0.776 | +| `c6ffe19d-0-GER40-w0-r1-s0-0` (fast=10) | `stop_distance=10.0` | 1387 | −0.0188 | −0.371 | 1.717 | −0.866 | +| `c6ffe19d-0-GER40-w0-r1-s0-0` (fast=20) | `stop_distance=10.0` | 1449 | 0.0354 | 0.719 | 1.602 | −0.904 | + +The R-unit semantics read coherently: under the fixed stop both tails compress +(`avg_win_r` 1.60–1.72 against 2.24–2.49, `avg_loss_r` −0.78 to −0.90 against +−0.97 to −1.12), which is what a stop that never adapts should do — the R unit +is a constant 10 points rather than a per-cycle volatility multiple, so the same +price excursion buys fewer R in either direction. Both regimes rank the same +winner (`fast.length=20`) with different raw metrics, and the record keys them +separately (`regime_ordinal` absent for the first regime, `1` for the second) — +compared, never pooled, as C10 requires. + +Trace dirs follow the documented ordinal rule: + +``` +runs/traces/c6ffe19d-0/33af5e38-GER40-w0/r_equity.json +runs/traces/c6ffe19d-0/33af5e38-GER40-w0-r1/r_equity.json +``` + +but the **family ids do not** — see finding B1. + +Regime validation refuses cleanly: + +``` +$ aura exec +aura: campaign document invalid: + risk[0]: stop length must be >= 1, k must be > 0, and a fixed-stop distance must be > 0 +exit=1 +$ aura exec +aura: campaign document: unknown variant `trailing`, expected one of `vol`, `vol_tf`, `fixed` +exit=1 +``` + +`emit` selectors were probed separately (short-window copies of the same doc): +`family_table` produces the per-member `{"family_id","report"}` lines, +`selection_report` the `{"selection_report":…}` line; the `{"campaign_run":…}` +line is unconditional. + +--- + +## Axis 3 — exec's refusal families + +Fixture: `hv_3_exec_refusals.sh`, replayable from inside `lab/`. Full output in +that script's own run; the branch table it establishes: + +| invocation | exit | class | +|---|---|---| +| op-script array as target | 2 | routing-seam content fault | +| `kind:"process"` document | 2 | routing-seam content fault | +| `kind:"widget"` document | 2 | routing-seam content fault | +| file that is not JSON | 2 | routing-seam content fault | +| missing file | 1 | missing state | +| well-formed unregistered 64-hex id | 1 | missing state | +| bare non-id token | 1 | missing state | +| open (free-knob) blueprint | 2 | blueprint-leg content fault | +| `--override` without `=` | 2 | argv fault | +| `--override nosuch.param=3` | 1 | blueprint-dependent | +| `--override fast.length=abc` | 2 | argv fault (**misdiagnosed** — see B2) | +| `--override fast.length=3.5` | 1 | blueprint-dependent | +| `--override` colliding with a declared axis | 2 | argv fault | +| `--tap` on a campaign target | 2 | argv fault | +| campaign document content fault (empty axis / bad emit / bad regime) | 1 | campaign-leg content fault | +| campaign completing with a failed cell | 3 | completed-with-failures | + +The verbatim prose that a script author would branch on, for the three new +routing-seam families: + +``` +aura: exec: blueprints/opscript-target.json: this document is an op-script (a JSON array of construction ops), not something exec can run directly — build it into a blueprint first with `aura graph build < blueprints/opscript-target.json`, then exec the result +aura: exec: blueprints/hv_2_process_sweep.json: this document's kind is "process", but exec executes only a campaign document or a fully-bound blueprint +aura: exec: blueprints/notjson.json: target file is not valid JSON: expected ident at line 1 column 2 +``` + +Every one names the target, the fault, and (where one exists) the next command. +The exit-3 case was confirmed against a weekend window with no GER40 bars: + +``` +aura: warning: cell (33af5e38…, GER40, [1725148800000, 1725235200000]) failed at stage 0: no data for instrument GER40 in window … — recorded, campaign continues +aura: campaign run 1 recorded: 1 cells, 1 failed (GER40: no_data) +exit=3 +``` + +--- + +## Axis 4 — zero-trade notes and the override identity + +### The two new notes + +`hv_4_flat.ops.json` is a deliberately flat strategy (`Const 0.0 → Bias`), so it +never opens a position. Executed bare, against the built-in synthetic stream: + +``` +$ aura exec blueprints/hv-flat.json +aura: note: the built-in synthetic stream (18 cycles) is a small smoke fixture; zero trades is expected here — bind real data via a campaign document's `data` section (`aura exec `) for a meaningful run +exit=0 +``` + +The same strategy over real GER40 bars, as `hv_4_campaign_zero_trade.json` (a +non-walk-forward `std::sweep`, two axis points, both trading zero times): + +``` +$ aura exec blueprints/hv_4_campaign_zero_trade.json +aura: note: the cell's 2 reports all recorded zero trades; its metrics are vacuous, not a break-even result +aura: campaign run 0 recorded: 1 cells +exit=0 +``` + +Both carry the benign `aura: note: ` marker and leave the exit code at 0 — the +grep-stable class C14 specifies, and the honest reading a consumer needs before +mistaking `expectancy_r: 0.0` for break-even. + +### Reference semantics under `--override` + +Four executions of the same registered blueprint (`ma-blend`, base content id +`ab8425…f7c6`), each with `--tap blend=record` so the trace store is written: + +| run | record-line `topology_hash` | `runs/traces/ma-blend/index.json` hash | manifest `params` | +|---|---|---|---| +| bare | `ab8425…f7c6` | `ab8425…f7c6` | `[]` | +| `--override bias.scale=1.0` (no-op) | `ab8425…f7c6` | `ab8425…f7c6` | `[["bias.scale",{"F64":1.0}]]` | +| `--override bias.scale=2.0` | `ab8425…f7c6` | `ab8425…f7c6` | `[["bias.scale",{"F64":2.0}]]` | +| `--override fast.length=4` | `ab8425…f7c6` | `ab8425…f7c6` | `[["fast.length",{"I64":4}]]` | + +This is exactly the revised C24 clause: the executed run stamps the **loaded +base document's** content id on both surfaces, the variation lives in `params` +(moved out of `defaults`), and the hash stays store-resolvable — `ab8425…f7c6` +is the id `graph register` printed. Record line and trace index never diverged. + +What the same four runs also show is that all four wrote into the *same* +`runs/traces/ma-blend/` directory, each overwriting the last — see finding S1. diff --git a/fieldtests/harvest-2026-07-26/hv_1_blend_taps.ops.json b/fieldtests/harvest-2026-07-26/hv_1_blend_taps.ops.json new file mode 100644 index 0000000..6bbfce3 --- /dev/null +++ b/fieldtests/harvest-2026-07-26/hv_1_blend_taps.ops.json @@ -0,0 +1,23 @@ +[ + {"op": "name", "name": "ma-blend"}, + {"op": "doc", "text": "weighted blend of an SMA crossover spread and a price-vs-slow momentum term"}, + {"op": "source", "role": "price", "kind": "F64"}, + {"op": "add", "type": "SMA", "name": "fast", "bind": {"length": {"I64": 3}}}, + {"op": "add", "type": "SMA", "name": "slow", "bind": {"length": {"I64": 10}}}, + {"op": "add", "type": "Sub", "name": "spread_node"}, + {"op": "add", "type": "Sub", "name": "mom"}, + {"op": "feed", "role": "price", "into": ["fast.series", "slow.series", "mom.lhs"]}, + {"op": "connect", "from": "fast.value", "to": "spread_node.lhs"}, + {"op": "connect", "from": "slow.value", "to": "spread_node.rhs"}, + {"op": "connect", "from": "slow.value", "to": "mom.rhs"}, + {"op": "add", "type": "LinComb", "name": "combo", + "args": {"arity": "2"}, + "bind": {"weights[0]": {"F64": 0.7}, "weights[1]": {"F64": 0.3}}}, + {"op": "connect", "from": "spread_node.value", "to": "combo.term[0]"}, + {"op": "connect", "from": "mom.value", "to": "combo.term[1]"}, + {"op": "add", "type": "Bias", "name": "bias", "bind": {"scale": {"F64": 1.0}}}, + {"op": "connect", "from": "combo.value", "to": "bias.signal"}, + {"op": "tap", "from": "spread_node.value", "as": "spread"}, + {"op": "tap", "from": "combo.value", "as": "blend"}, + {"op": "expose", "from": "bias.bias", "as": "bias"} +] diff --git a/fieldtests/harvest-2026-07-26/hv_1b_unnamed_taps.ops.json b/fieldtests/harvest-2026-07-26/hv_1b_unnamed_taps.ops.json new file mode 100644 index 0000000..e9c38e9 --- /dev/null +++ b/fieldtests/harvest-2026-07-26/hv_1b_unnamed_taps.ops.json @@ -0,0 +1,121 @@ +[ + { + "op": "doc", + "text": "weighted blend of an SMA crossover spread and a price-vs-slow momentum term" + }, + { + "op": "source", + "role": "price", + "kind": "F64" + }, + { + "op": "add", + "type": "SMA", + "name": "fast", + "bind": { + "length": { + "I64": 3 + } + } + }, + { + "op": "add", + "type": "SMA", + "name": "slow", + "bind": { + "length": { + "I64": 10 + } + } + }, + { + "op": "add", + "type": "Sub", + "name": "spread_node" + }, + { + "op": "add", + "type": "Sub", + "name": "mom" + }, + { + "op": "feed", + "role": "price", + "into": [ + "fast.series", + "slow.series", + "mom.lhs" + ] + }, + { + "op": "connect", + "from": "fast.value", + "to": "spread_node.lhs" + }, + { + "op": "connect", + "from": "slow.value", + "to": "spread_node.rhs" + }, + { + "op": "connect", + "from": "slow.value", + "to": "mom.rhs" + }, + { + "op": "add", + "type": "LinComb", + "name": "combo", + "args": { + "arity": "2" + }, + "bind": { + "weights[0]": { + "F64": 0.7 + }, + "weights[1]": { + "F64": 0.3 + } + } + }, + { + "op": "connect", + "from": "spread_node.value", + "to": "combo.term[0]" + }, + { + "op": "connect", + "from": "mom.value", + "to": "combo.term[1]" + }, + { + "op": "add", + "type": "Bias", + "name": "bias", + "bind": { + "scale": { + "F64": 1.0 + } + } + }, + { + "op": "connect", + "from": "combo.value", + "to": "bias.signal" + }, + { + "op": "tap", + "from": "spread_node.value", + "as": "spread" + }, + { + "op": "tap", + "from": "combo.value", + "as": "blend" + }, + { + "op": "expose", + "from": "bias.bias", + "as": "bias" + } +] \ No newline at end of file diff --git a/fieldtests/harvest-2026-07-26/hv_1c_named_notaps.ops.json b/fieldtests/harvest-2026-07-26/hv_1c_named_notaps.ops.json new file mode 100644 index 0000000..d5c66dc --- /dev/null +++ b/fieldtests/harvest-2026-07-26/hv_1c_named_notaps.ops.json @@ -0,0 +1,115 @@ +[ + { + "op": "name", + "name": "ma-blend-notaps" + }, + { + "op": "doc", + "text": "weighted blend of an SMA crossover spread and a price-vs-slow momentum term" + }, + { + "op": "source", + "role": "price", + "kind": "F64" + }, + { + "op": "add", + "type": "SMA", + "name": "fast", + "bind": { + "length": { + "I64": 3 + } + } + }, + { + "op": "add", + "type": "SMA", + "name": "slow", + "bind": { + "length": { + "I64": 10 + } + } + }, + { + "op": "add", + "type": "Sub", + "name": "spread_node" + }, + { + "op": "add", + "type": "Sub", + "name": "mom" + }, + { + "op": "feed", + "role": "price", + "into": [ + "fast.series", + "slow.series", + "mom.lhs" + ] + }, + { + "op": "connect", + "from": "fast.value", + "to": "spread_node.lhs" + }, + { + "op": "connect", + "from": "slow.value", + "to": "spread_node.rhs" + }, + { + "op": "connect", + "from": "slow.value", + "to": "mom.rhs" + }, + { + "op": "add", + "type": "LinComb", + "name": "combo", + "args": { + "arity": "2" + }, + "bind": { + "weights[0]": { + "F64": 0.7 + }, + "weights[1]": { + "F64": 0.3 + } + } + }, + { + "op": "connect", + "from": "spread_node.value", + "to": "combo.term[0]" + }, + { + "op": "connect", + "from": "mom.value", + "to": "combo.term[1]" + }, + { + "op": "add", + "type": "Bias", + "name": "bias", + "bind": { + "scale": { + "F64": 1.0 + } + } + }, + { + "op": "connect", + "from": "combo.value", + "to": "bias.signal" + }, + { + "op": "expose", + "from": "bias.bias", + "as": "bias" + } +] \ No newline at end of file diff --git a/fieldtests/harvest-2026-07-26/hv_2_cross.ops.json b/fieldtests/harvest-2026-07-26/hv_2_cross.ops.json new file mode 100644 index 0000000..8a3e3e5 --- /dev/null +++ b/fieldtests/harvest-2026-07-26/hv_2_cross.ops.json @@ -0,0 +1,14 @@ +[ + {"op": "name", "name": "hv-cross"}, + {"op": "doc", "text": "SMA crossover bias: fast minus slow, clamped into the bias contract"}, + {"op": "source", "role": "price", "kind": "F64"}, + {"op": "add", "type": "SMA", "name": "fast"}, + {"op": "add", "type": "SMA", "name": "slow", "bind": {"length": {"I64": 30}}}, + {"op": "feed", "role": "price", "into": ["fast.series", "slow.series"]}, + {"op": "add", "type": "Sub", "name": "sub"}, + {"op": "connect", "from": "fast.value", "to": "sub.lhs"}, + {"op": "connect", "from": "slow.value", "to": "sub.rhs"}, + {"op": "add", "type": "Bias", "name": "bias", "bind": {"scale": {"F64": 0.5}}}, + {"op": "connect", "from": "sub.value", "to": "bias.signal"}, + {"op": "expose", "from": "bias.bias", "as": "bias"} +] diff --git a/fieldtests/harvest-2026-07-26/hv_3_exec_refusals.sh b/fieldtests/harvest-2026-07-26/hv_3_exec_refusals.sh new file mode 100644 index 0000000..2fc27df --- /dev/null +++ b/fieldtests/harvest-2026-07-26/hv_3_exec_refusals.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# hv_3 — exec's refusal families as a script author meets them. +# +# Run from inside the sibling `lab/` project (a plain `aura new` scaffold), with +# `aura` on PATH: +# +# cd lab && sh ../hv_3_exec_refusals.sh +# +# The question this fixture answers: can a script author branch on exit code +# alone, without parsing stderr? Each block prints the invocation, its exit +# code, and its verbatim stderr. + +set -u + +probe() { + printf -- '--- $ aura exec %s\n' "$*" + aura exec "$@" >/dev/null 2>/tmp/aura-hv3.err + printf 'exit=%s\n' "$?" + cat /tmp/aura-hv3.err + printf '\n' +} + +# Fixtures the probes need, built in place. +cp ../hv_1_blend_taps.ops.json blueprints/opscript-target.json +printf 'this is not json at all\n' >blueprints/notjson.json +printf '{"format_version":1,"kind":"widget","name":"x"}' >blueprints/wrongkind.json + +# --- target classification at the routing seam (C14's pinned partition) --- +probe blueprints/opscript-target.json # op-script array +probe blueprints/hv_2_process_sweep.json # kind:"process" +probe blueprints/wrongkind.json # kind:"widget" +probe blueprints/notjson.json # not JSON at all +probe blueprints/does-not-exist.json # no such file +probe 0000000000000000000000000000000000000000000000000000000000000000 # unregistered id +probe some-random-token # neither file nor id + +# --- the blueprint leg's own gates --- +probe blueprints/hv-cross.json # open (free-knob) blueprint +probe blueprints/hv-cross.json --override fast.length=7 # ... and an override that does not close it + +# --- --override argument faults --- +probe blueprints/ma-blend.json --override fast.length # no '=' +probe blueprints/ma-blend.json --override nosuch.param=3 # no such param +probe blueprints/ma-blend.json --override fast.length=abc # unparseable value +probe blueprints/ma-blend.json --override fast.length=3.5 # wrong scalar kind + +# --- flags that do not apply to the chosen leg --- +probe blueprints/hv_2_campaign_two_regimes.json --tap spread=mean diff --git a/fieldtests/harvest-2026-07-26/hv_4_flat.ops.json b/fieldtests/harvest-2026-07-26/hv_4_flat.ops.json new file mode 100644 index 0000000..fe96110 --- /dev/null +++ b/fieldtests/harvest-2026-07-26/hv_4_flat.ops.json @@ -0,0 +1,11 @@ +[ + {"op": "name", "name": "hv-flat"}, + {"op": "doc", "text": "deliberately flat bias: emits a constant zero signal, so the executor never opens a position"}, + {"op": "source", "role": "price", "kind": "F64"}, + {"op": "add", "type": "Const", "name": "flat", "bind": {"value": {"F64": 0.0}}}, + {"op": "feed", "role": "price", "into": ["flat.clock"]}, + {"op": "add", "type": "Bias", "name": "bias", "bind": {"scale": {"F64": 1.0}}}, + {"op": "connect", "from": "flat.value", "to": "bias.signal"}, + {"op": "tap", "from": "flat.value", "as": "flat_signal"}, + {"op": "expose", "from": "bias.bias", "as": "bias"} +] diff --git a/fieldtests/harvest-2026-07-26/lab/.gitignore b/fieldtests/harvest-2026-07-26/lab/.gitignore new file mode 100644 index 0000000..306cbf0 --- /dev/null +++ b/fieldtests/harvest-2026-07-26/lab/.gitignore @@ -0,0 +1 @@ +/runs diff --git a/fieldtests/harvest-2026-07-26/lab/Aura.toml b/fieldtests/harvest-2026-07-26/lab/Aura.toml new file mode 100644 index 0000000..d45dade --- /dev/null +++ b/fieldtests/harvest-2026-07-26/lab/Aura.toml @@ -0,0 +1,4 @@ +# Static project context only (C17); paths only. +[paths] +runs = "runs" +# data = "/path/to/archive" # the recorded-data root; defaults to the built-in path diff --git a/fieldtests/harvest-2026-07-26/lab/CLAUDE.md b/fieldtests/harvest-2026-07-26/lab/CLAUDE.md new file mode 100644 index 0000000..5f60288 --- /dev/null +++ b/fieldtests/harvest-2026-07-26/lab/CLAUDE.md @@ -0,0 +1,24 @@ +# lab — an aura research project (data-only) + +This directory is an aura project: blueprints + research documents over the +std vocabulary, anchored by `Aura.toml`. There is no crate and no build step. + +- Run: `aura exec blueprints/signal.json` (single smoke run, synthetic stream + — the starter is closed; all params bound; bound values are defaults — + `--override NODE.PARAM=VALUE` may override one for this run (#246)) +- Campaign: `aura exec ` executes a registered campaign + document (file or content id) — the multi-cell/axis surface +- Axes: `aura graph introspect --params blueprints/signal.json` lists the + open + bound-overridable axes, RAW `.` names (#328) +- Native nodes: when the project needs its first project-specific node, + `aura nodes new ` scaffolds a node crate beside this project and + attaches it via `[nodes]` in `Aura.toml` (build it with `cargo build`). +- Topology is data (`blueprints/*.json`); results land in `runs/`. +- Execution model: a strategy emits a bias in [-1,+1] per cycle, held as the + continuously-tracked target position; a protective stop defines the risk + 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: process/campaign documents are authored directly with + `aura process` / `aura campaign`, growing one from a bare `{}` via + `aura campaign introspect --unwired`. diff --git a/fieldtests/harvest-2026-07-26/lab/blueprints/hv-cross.json b/fieldtests/harvest-2026-07-26/lab/blueprints/hv-cross.json new file mode 100644 index 0000000..e67b37d --- /dev/null +++ b/fieldtests/harvest-2026-07-26/lab/blueprints/hv-cross.json @@ -0,0 +1 @@ +{"format_version":1,"blueprint":{"name":"hv-cross","doc":"SMA crossover bias: fast minus slow, clamped into the bias contract","nodes":[{"primitive":{"type":"SMA","name":"fast"}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":30}}]}},{"primitive":{"type":"Sub","name":"sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}} \ No newline at end of file diff --git a/fieldtests/harvest-2026-07-26/lab/blueprints/hv-flat.json b/fieldtests/harvest-2026-07-26/lab/blueprints/hv-flat.json new file mode 100644 index 0000000..1fe3c9d --- /dev/null +++ b/fieldtests/harvest-2026-07-26/lab/blueprints/hv-flat.json @@ -0,0 +1 @@ +{"format_version":1,"blueprint":{"name":"hv-flat","doc":"deliberately flat bias: emits a constant zero signal, so the executor never opens a position","nodes":[{"primitive":{"type":"Const","name":"flat","bound":[{"pos":0,"name":"value","kind":"F64","value":{"F64":0.0}}]}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":1.0}}]}}],"edges":[{"from":0,"to":1,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0}],"source":"F64"}],"output":[{"node":1,"field":0,"name":"bias"}],"taps":[{"name":"flat_signal","from":{"node":0,"field":0}}]}} \ No newline at end of file diff --git a/fieldtests/harvest-2026-07-26/lab/blueprints/hv_2_campaign_two_regimes.json b/fieldtests/harvest-2026-07-26/lab/blueprints/hv_2_campaign_two_regimes.json new file mode 100644 index 0000000..1323e91 --- /dev/null +++ b/fieldtests/harvest-2026-07-26/lab/blueprints/hv_2_campaign_two_regimes.json @@ -0,0 +1,24 @@ +{ + "format_version": 1, + "kind": "campaign", + "name": "hv-ger40-cross-vol-vs-fixed", + "seed": 7, + "data": { + "instruments": ["GER40"], + "windows": [ { "from_ms": 1725148800000, "to_ms": 1727740800000 } ] + }, + "risk": [ + { "vol": { "length": 3, "k": 2.0 } }, + { "fixed": { "distance": 10.0 } } + ], + "strategies": [ + { + "ref": { "content_id": "33af5e380d5e67d9b5464b4b9644419db1cdd2480104bd520592f6addcaeef34" }, + "axes": { + "fast.length": { "kind": "I64", "values": [5, 10, 20] } + } + } + ], + "process": { "ref": { "content_id": "c7fb729490770305d657e1e78d8daf12b1cd4dab990aea8cf4620c0b369a3c48" } }, + "presentation": { "persist_taps": ["r_equity"], "emit": ["family_table", "selection_report"] } +} diff --git a/fieldtests/harvest-2026-07-26/lab/blueprints/hv_2_process_sweep.json b/fieldtests/harvest-2026-07-26/lab/blueprints/hv_2_process_sweep.json new file mode 100644 index 0000000..8c51c71 --- /dev/null +++ b/fieldtests/harvest-2026-07-26/lab/blueprints/hv_2_process_sweep.json @@ -0,0 +1,9 @@ +{ + "format_version": 1, + "kind": "process", + "name": "hv-sweep-sqn", + "description": "Single-stage sweep ranked by SQN — the minimum methodology for comparing two stop regimes.", + "pipeline": [ + { "block": "std::sweep", "metric": "sqn", "select": "argmax" } + ] +} diff --git a/fieldtests/harvest-2026-07-26/lab/blueprints/hv_4_campaign_zero_trade.json b/fieldtests/harvest-2026-07-26/lab/blueprints/hv_4_campaign_zero_trade.json new file mode 100644 index 0000000..93637b5 --- /dev/null +++ b/fieldtests/harvest-2026-07-26/lab/blueprints/hv_4_campaign_zero_trade.json @@ -0,0 +1,44 @@ +{ + "format_version": 1, + "kind": "campaign", + "name": "hv-ger40-flat-zero-trade", + "seed": 3, + "data": { + "instruments": [ + "GER40" + ], + "windows": [ + { + "from_ms": 1725235200000, + "to_ms": 1725408000000 + } + ] + }, + "strategies": [ + { + "ref": { + "content_id": "6075193021f0226fdede891814401fc4abc51e19f4b7e2605113799fd781a6c7" + }, + "axes": { + "bias.scale": { + "kind": "F64", + "values": [ + 1.0, + 2.0 + ] + } + } + } + ], + "process": { + "ref": { + "content_id": "c7fb729490770305d657e1e78d8daf12b1cd4dab990aea8cf4620c0b369a3c48" + } + }, + "presentation": { + "persist_taps": [], + "emit": [ + "family_table" + ] + } +} diff --git a/fieldtests/harvest-2026-07-26/lab/blueprints/ma-blend.json b/fieldtests/harvest-2026-07-26/lab/blueprints/ma-blend.json new file mode 100644 index 0000000..8a202a4 --- /dev/null +++ b/fieldtests/harvest-2026-07-26/lab/blueprints/ma-blend.json @@ -0,0 +1 @@ +{"format_version":2,"blueprint":{"name":"ma-blend","doc":"weighted blend of an SMA crossover spread and a price-vs-slow momentum term","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":3}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":10}}]}},{"primitive":{"type":"Sub","name":"spread_node"}},{"primitive":{"type":"Sub","name":"mom"}},{"primitive":{"type":"LinComb","name":"combo","args":[{"name":"arity","value":"2"}],"bound":[{"pos":0,"name":"weights[0]","kind":"F64","value":{"F64":0.7}},{"pos":1,"name":"weights[1]","kind":"F64","value":{"F64":0.3}}]}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":1.0}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":1,"to":3,"slot":1,"from_field":0},{"from":2,"to":4,"slot":0,"from_field":0},{"from":3,"to":4,"slot":1,"from_field":0},{"from":4,"to":5,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0},{"node":3,"slot":0}],"source":"F64"}],"output":[{"node":5,"field":0,"name":"bias"}],"taps":[{"name":"spread","from":{"node":2,"field":0}},{"name":"blend","from":{"node":4,"field":0}}]}} \ No newline at end of file diff --git a/fieldtests/harvest-2026-07-26/lab/blueprints/notaps.json b/fieldtests/harvest-2026-07-26/lab/blueprints/notaps.json new file mode 100644 index 0000000..fdb03cf --- /dev/null +++ b/fieldtests/harvest-2026-07-26/lab/blueprints/notaps.json @@ -0,0 +1 @@ +{"format_version":2,"blueprint":{"name":"ma-blend-notaps","doc":"weighted blend of an SMA crossover spread and a price-vs-slow momentum term","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":3}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":10}}]}},{"primitive":{"type":"Sub","name":"spread_node"}},{"primitive":{"type":"Sub","name":"mom"}},{"primitive":{"type":"LinComb","name":"combo","args":[{"name":"arity","value":"2"}],"bound":[{"pos":0,"name":"weights[0]","kind":"F64","value":{"F64":0.7}},{"pos":1,"name":"weights[1]","kind":"F64","value":{"F64":0.3}}]}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":1.0}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":1,"to":3,"slot":1,"from_field":0},{"from":2,"to":4,"slot":0,"from_field":0},{"from":3,"to":4,"slot":1,"from_field":0},{"from":4,"to":5,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0},{"node":3,"slot":0}],"source":"F64"}],"output":[{"node":5,"field":0,"name":"bias"}]}} \ No newline at end of file diff --git a/fieldtests/harvest-2026-07-26/lab/blueprints/signal.json b/fieldtests/harvest-2026-07-26/lab/blueprints/signal.json new file mode 100644 index 0000000..46120a2 --- /dev/null +++ b/fieldtests/harvest-2026-07-26/lab/blueprints/signal.json @@ -0,0 +1 @@ +{"format_version":1,"blueprint":{"name":"lab_signal","doc":"fast/slow SMA difference clamped into a directional bias","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":2}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":4}}]}},{"primitive":{"type":"Sub"}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":0.5}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":2,"to":3,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0}],"source":"F64"}],"output":[{"node":3,"field":0,"name":"bias"}]}} \ No newline at end of file diff --git a/fieldtests/harvest-2026-07-26/lab/blueprints/unnamed.json b/fieldtests/harvest-2026-07-26/lab/blueprints/unnamed.json new file mode 100644 index 0000000..f2db3e8 --- /dev/null +++ b/fieldtests/harvest-2026-07-26/lab/blueprints/unnamed.json @@ -0,0 +1 @@ +{"format_version":2,"blueprint":{"name":"graph","doc":"weighted blend of an SMA crossover spread and a price-vs-slow momentum term","nodes":[{"primitive":{"type":"SMA","name":"fast","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":3}}]}},{"primitive":{"type":"SMA","name":"slow","bound":[{"pos":0,"name":"length","kind":"I64","value":{"I64":10}}]}},{"primitive":{"type":"Sub","name":"spread_node"}},{"primitive":{"type":"Sub","name":"mom"}},{"primitive":{"type":"LinComb","name":"combo","args":[{"name":"arity","value":"2"}],"bound":[{"pos":0,"name":"weights[0]","kind":"F64","value":{"F64":0.7}},{"pos":1,"name":"weights[1]","kind":"F64","value":{"F64":0.3}}]}},{"primitive":{"type":"Bias","name":"bias","bound":[{"pos":0,"name":"scale","kind":"F64","value":{"F64":1.0}}]}}],"edges":[{"from":0,"to":2,"slot":0,"from_field":0},{"from":1,"to":2,"slot":1,"from_field":0},{"from":1,"to":3,"slot":1,"from_field":0},{"from":2,"to":4,"slot":0,"from_field":0},{"from":3,"to":4,"slot":1,"from_field":0},{"from":4,"to":5,"slot":0,"from_field":0}],"input_roles":[{"name":"price","targets":[{"node":0,"slot":0},{"node":1,"slot":0},{"node":3,"slot":0}],"source":"F64"}],"output":[{"node":5,"field":0,"name":"bias"}],"taps":[{"name":"spread","from":{"node":2,"field":0}},{"name":"blend","from":{"node":4,"field":0}}]}} \ No newline at end of file